17. Sockets

Introduction

# Socket Programming (tcp/ip and udp/ip)

import socket

# Convert human readable form to 32 bit value
packed_ip = socket.inet_aton("208.146.240.1")
packed_ip = socket.inet_aton("www.oreilly.com")

# Convert 32 bit value to ip adress
ip_adress = socket.inet_ntoa(packed_ip)

# Create socket object
socketobj = socket(family, type) # Example socket.AF_INT, socket.SOCK_STREAM
       
# Get socketname
socketobj.getsockname() # Example, get port adress of client

Writing a TCP Client


# Example: Connect to a server (tcp)
# Connect to a smtp server at localhost and send an email.
# For real applications you should use smtplib.

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("localhost", 25)) # SMTP
print s.recv(1024)
s.send("mail from: <pleac@localhost>\n")
print s.recv(1024)
s.send("rcpt to: <guettli@localhost>\n")
print s.recv(1024)
s.send("data\n")
print s.recv(1024)
s.send("From: Python Lover\nSubject: Python is better then perl\n\nYES!\n.\n")
print s.recv(1024)
s.close()

Writing a TCP Server


# Create a Server, calling handler for every client
# You can test it with "telnet localhost 1029"

from SocketServer import TCPServer
from SocketServer import BaseRequestHandler

class MyHandler(BaseRequestHandler):
    def handle(self):
        print "I got an request"
        
server = TCPServer(("127.0.0.1", 1029), MyHandler)
server.serve_forever()

Communicating over TCP

# This is the continuation of 17.2

import time
from SocketServer import TCPServer
from SocketServer import BaseRequestHandler

class MyHandler(BaseRequestHandler):
    def handle(self):
        # self.request is the socket object
        print "%s I got an request from ip=%s port=%s" % (
            time.strftime("%Y-%m-%d %H:%M:%S"),
            self.client_address[0],
            self.client_address[1]
            )
        self.request.send("What is your name?\n")
        bufsize=1024
        response=self.request.recv(bufsize).strip() # or recv(bufsize, flags)
        data_to_send="Welcome %s!\n" % response
        self.request.send(data_to_send) # or send(data, flags)
        print "%s connection finnished" % self.client_address[0]
        
server = TCPServer(("127.0.0.1", 1028), MyHandler)
server.serve_forever()

# -----------------
# Using select

import select
import socket

in_list = []
in_list.append(mysocket)
in_list.append(myfile)
# ...

out_list = []
out_list.append(...)

except_list = []
except_list.append(...)

(in_, out_, exc_) = select.select(in_list, out_list, except_list, timeout)

for fd in in_:
    print "Can read", fd
for fd in out_:
    print "Can write", fd
for fd in exc_:
    print "Exception on", fd

# Missing: setting TCP_NODELAY

Setting Up a UDP Client


import socket
# Set up a UDP socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# send 
MSG = 'Hello'
HOSTNAME = '127.0.0.1'
PORTNO = 10000
s.connect((HOSTNAME, PORTNO))
if len(MSG) != s.send(MSG):
    # where to get error message "$!".
    print "cannot send to %s(%d):" % (HOSTNAME,PORTNO)
    raise SystemExit(1)
MAXLEN = 1024
(data,addr) = s.recvfrom(MAXLEN)
s.close()
print '%s(%d) said "%s"' % (addr[0],addr[1], data)

# download the following standalone program
#!/usr/bin/python
# clockdrift - compare another system's clock with this one

import socket
import struct
import sys
import time

if len(sys.argv)>1:
    him = sys.argv[1]
else:
    him = '127.1'

SECS_of_70_YEARS = 2208988800

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect((him,socket.getservbyname('time','udp')))
s.send('')
(ptime, src) = s.recvfrom(4)
host = socket.gethostbyaddr(src[0])
delta = struct.unpack("!L", ptime)[0] - SECS_of_70_YEARS - time.time()
print "Clock on %s is %d seconds ahead of this one." % (host[0], delta)

Setting Up a UDP Server


import socket
import sys

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
    s.bind(('', server_port))
except socket.error, err:
    print "Couldn't be a udp server on port %d : %s" % (
            server_port, err)
    raise SystemExit

while True:
    datagram = s.recv(MAX_TO_READ)
    if not datagram:
        break
    # do something
s.close()

# or 
import SocketServer

class handler(SocketServer.DatagramRequestHandler):
    def handle(self):
        # do something (with self.request[0])

s = SocketServer.UDPServer(('',10000), handler)
s.serve_forever()

# download the following standalone program
#!/usr/bin/python
# udpqotd - UDP message server

import SocketServer

PORTNO = 5151

class handler(SocketServer.DatagramRequestHandler):
    def handle(self):
        newmsg = self.rfile.readline().rstrip()
        print "Client %s said ``%s''" % (self.client_address[0], newmsg)
        self.wfile.write(self.server.oldmsg)
        self.server.oldmsg = newmsg

s = SocketServer.UDPServer(('',PORTNO), handler)
print "Awaiting UDP messages on port %d" % PORTNO
s.oldmsg = "This is the starting message."
s.serve_forever()


# download the following standalone program
#!/usr/bin/python
# udpmsg - send a message to the udpquotd server

import socket
import sys

MAXLEN = 1024
PORTNO = 5151
TIMEOUT = 5

server_host = sys.argv[1]
msg = " ".join(sys.argv[2:])

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(TIMEOUT)
sock.connect((server_host, PORTNO))
sock.send(msg)
try:
    msg = sock.recv(MAXLEN)
    ipaddr, port = sock.getpeername()
    hishost = socket.gethostbyaddr(ipaddr)
    print "Server %s responded ``%s''" % ( hishost[0], msg)
except:
    print "recv from %s failed (timeout or no server running)." % (
            server_host )
sock.close()

Using UNIX Domain Sockets


import socket
import os, os.path

if os.path.exists("/tmp/mysock"):
    os.remove("/tmp/mysock")
        
server = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
server.bind("/tmp/mysock")

client = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
client.connect("/tmp/mysock")
        

Identifying the Other End of a Socket


ipaddr, port = s.getpeername()
hostname, aliaslist, ipaddrlist = socket.gethostbyaddr(ipaddr)
ipaddr = socket.gethostbyname('www.python.org')
# '194.109.137.226'
hostname, aliaslist, ipaddrlist = socket.gethostbyname_ex('www.python.org')
# ('fang.python.org', ['www.python.org'], ['194.109.137.226'])
socket.gethostbyname_ex('www.google.org')
# ('www.l.google.com', ['www.google.org', 'www.google.com'], 
#  ['64.233.161.147','64.233.161.104', '64.233.161.99'])

Finding Your Own Name and Address


import os

kernel, hostname, release, version, hardware = os.uname()

import socket

address = socket.gethostbyname(hostname)
hostname = socket.gethostbyaddr(address)
hostname, aliaslist, ipaddrlist = socket.gethostbyname_ex(hostname)
# e.g. ('lx3.local', ['lx3', 'b70'], ['192.168.0.13', '192.168.0.70'])

Closing a Socket After Forking


socket.shutdown(0)   # Further receives are disallowed
socket.shutdown(1)   # Further sends are disallowed.
socket.shutdown(2)   # Further sends and receives are disallowed.

#

server.send("my request\n")  # send some data
server.shutdown(1)           # send eof; no more writing
answer = server.recv(1000)   # but you can still read

Writing Bidirectional Clients

# @@INCOMPLETE@@
# @@INCOMPLETE@@

Forking Servers

# @@INCOMPLETE@@
# @@INCOMPLETE@@

Pre-Forking Servers

# @@INCOMPLETE@@
# @@INCOMPLETE@@

Non-Forking Servers

# @@INCOMPLETE@@
# @@INCOMPLETE@@

Writing a Multi-Homed Server

# @@INCOMPLETE@@
# @@INCOMPLETE@@

Making a Daemon Server

# @@INCOMPLETE@@
# @@INCOMPLETE@@

Restarting a Server on Demand

#------------------------------
# Restart programm on signal SIGHUP
# Script must be executable: chmod a+x foo.py

#!/usr/bin/env python
import os
import sys
import time
import signal

def phoenix(signum, frame):
    print "Restarting myself: %s %s" % (self, args)
    os.execv(self, args)

self = os.path.abspath(sys.argv[0])
args = sys.argv[:]
signal.signal(signal.SIGHUP, phoenix)

while True:
    print "work"
    time.sleep(1)

#--------------------
# Read config file on SIGHUP
import signal

config_file = "/usr/local/etc/myprog/server_conf.py"

def read_config():
    execfile(config_file)

signal.signal(signal.SIGHUP, read_config)

Program: backsniff


# chroot

import os

try:
    os.chroot("/var/daemon")
except Exception:
    print "Could not chroot"
    raise SystemExit(1)

#-----------------------------
# fork (Unix): Create a new process
# if pid == 0 --> parent process
# else child process

import os

pid = os.fork()
if pid:
    print "I am the new child %s" % pid
    raise SystemExit
else:
    print "I am still the parent"
    

# ----------------------------
# setsid (Unix): Create a new session
import os
id=os.setsid()

# ----------------------------
# Work until INT TERM or HUP signal is received
import time
import signal

time_to_die = 0

def sighandler(signum, frame):
    print "time to die"
    global time_to_die
    time_to_die = 1

signal.signal(signal.SIGINT, sighandler)
signal.signal(signal.SIGTERM, sighandler)
signal.signal(signal.SIGHUP, sighandler)

while not time_to_die:
    print "work"
    time.sleep(1)

Program: fwdport

# @@INCOMPLETE@@
# @@INCOMPLETE@@