root/tags/jjigw-0.1/spidentd.py

Revision 81, 14.4 kB (checked in by jajcus, 5 years ago)

- installation (make install)

Line 
1 #!/usr/bin/python
2
3 """ Simple Programmable Ident (RFC1413) Daemon """
4
5 import sys
6 sys.path=sys.path[1:]
7
8 import signal
9 import socket
10 import select
11 import threading
12 import os
13 import errno
14 import pwd
15 import grp
16 import getopt
17 import re
18
19 class Driver:
20         def __init__(self):
21                 pass
22         def lookup(self,localport,localip,remoteport,remoteip):
23                 return None
24
25 class NoUserDriver(Driver):
26         def lookup(self,localport,localip,remoteport,remoteip):
27                 return "ERROR:NO-USER"
28
29 class HiddenUserDriver(Driver):
30         def lookup(self,localport,localip,remoteport,remoteip):
31                 return "ERROR:HIDDEN-USER"
32
33 class FailDriver(Driver):
34         def lookup(self,localport,localip,remoteport,remoteip):
35                 return "ERROR:UNKNOWN-ERROR"
36
37 class StaticDriver(Driver):
38         def __init__(self,reply):
39                 self.reply=reply
40         def lookup(self,localport,localip,remoteport,remoteip):
41                 return self.reply
42
43 class Mapping:
44         def __init__(self):
45                 pass
46         def lookup(self,user):
47                 return user
48
49 class FileMapping(Mapping):
50         def __init__(self,path):
51                 self.mapping={}
52                 for l in file(path).xreadlines():
53                         l=l.strip()
54                         if not l:
55                                 continue
56                         key,val=l.split(":")
57                         self.mapping[key]=val
58         def lookup(self,user):
59                 return self.mapping.get(user,user)
60
61 class RealDriver(Driver):
62         def lookup(self,localport,localip,remoteport,remoteip):
63                 try:
64                         f=file("/proc/net/tcp","r")
65                 except IOError:
66                         print >>sys.stderr,"Couldn't open /proc/net/tcp"
67                         return None
68                 f.readline()
69                 li=[long(i) for i in localip.split(".")]
70                 localip=(li[0]<<24)+(li[1]<<16)+(li[2]<<8)+li[3]
71                 ri=[long(i) for i in remoteip.split(".")]
72                 remoteip=(ri[0]<<24)+(ri[1]<<16)+(ri[2]<<8)+ri[3]
73                 for l in f.xreadlines():
74                         sp=l.split()
75                         if len(sp)<8:
76                                 continue
77                         locip,locport=sp[1].split(":")
78                         locip=socket.htonl(long(locip,16))&0xffffffffL
79                         locport=int(locport,16)
80                         remip,remport=sp[2].split(":")
81                         remip=socket.htonl(long(remip,16))&0xffffffffL
82                         remport=int(remport,16)
83                         if (localip,localport,remoteip,remoteport)!=(locip,locport,remip,remport):
84                                 continue
85                         uid=int(sp[7])
86                         try:
87                                 pw=pwd.getpwuid(uid)
88                                 return pw[0]
89                         except:
90                                 return "ERROR:NO-USER"
91                 return None
92
93 class SocketDriverClient:
94         add_re=re.compile(r"add (\d+\.\d+\.\d+\.\d+):(\d+) (\d+\.\d+\.\d+\.\d+):(\d+) (.*)")
95         remove_re=re.compile(r"remove (\d+\.\d+\.\d+\.\d+):(\d+) (\d+\.\d+\.\d+\.\d+):(\d+)")
96         def __init__(self,driver,sock):
97                 self.socket=sock
98                 self.driver=driver
99                 self.thread=threading.Thread(target=self.run_thread)
100                 self.thread.setDaemon(1)
101                 self.conn_users={}
102                 self.buf=""
103                 self.thread.start()
104
105         def run_thread(self):
106                 try:
107                         self._run_thread()
108                 finally:
109                         self.conn_users={}
110                         try:
111                                 self.driver.clients.remove(self)
112                         except ValueError:
113                                 pass
114                         try:
115                                 self.sock.close()
116                         except:
117                                 pass
118
119         def _run_thread(self):
120                 global exit
121                 while not exit:
122                         l=self.read_line()
123                         if not l:
124                                 break
125                         print >>sys.stderr,"Got %r on programming socket" % (l,)
126                         m=self.add_re.match(l)
127                         if m:
128                                 localip,localport,remoteip,remoteport,user=m.groups()
129                                 self.conn_users[(int(localport),localip,
130                                                 int(remoteport),remoteip)]=user
131                                 continue
132                         m=self.remove_re.match(l)
133                         if m:
134                                 localip,localport,remoteip,remoteport=m.groups()
135                                 try:
136                                         del self.conn_users[(int(localport),localip,
137                                                         int(remoteport),remoteip)]
138                                 except KeyError:
139                                         pass
140                                 continue
141                         print >>sys.stderr,"Bad protocol on programming socket"
142
143         def read_line(self):
144                 while 1:
145                         if "\n" in self.buf:
146                                 line,self.buf=self.buf.split("\n",1)
147                                 return line
148                         if len(self.buf)>1024:
149                                 print >>sys.stderr,"Input line too long: %r" % (self.buf,)
150                                 return None
151                         r=self.socket.recv(1024)
152                         if not r:
153                                 return None
154                         self.buf+=r
155
156
157 class SocketDriver(Driver):
158         def __init__(self,path):
159                 global socketmode,socketgroup,user,group
160                 self.socket=socket.socket(socket.AF_UNIX)
161                 try:
162                         os.unlink(path)
163                 except:
164                         pass
165                 self.socket.bind(path)
166                 if socketmode is not None:
167                         os.chmod(path,socketmode)
168                 if os.getuid()==0:
169                         gid=None
170                         if socketgroup is not None:
171                                 gid=grp.getgrnam(socketgroup)[2]
172                         elif group is not None:
173                                 gid=grp.getgrnam(group)[2]
174                         elif user is not None:
175                                 gid=pwd.getpwname(user)[3]
176                         if gid:
177                                 os.chown(path,0,gid)
178                 self.socket.listen(1)
179                 self.clients=[]
180                 register_listening_socket(self.socket,self.accept_connection)
181         def lookup(self,localport,localip,remoteport,remoteip):
182                 for c in self.clients:
183                         connuser=c.conn_users.get((localport,localip,remoteport,remoteip))
184                         if connuser:
185                                 return connuser
186                 return None
187         def accept_connection(self,sock):
188                 sock,addr=self.socket.accept()
189                 print >>sys.stderr,"Client connection from: %r" % (addr,)
190                 self.clients.append(SocketDriverClient(self,sock))
191
192 listening_socket_handlers={}
193 def register_listening_socket(sock,handler):
194         global listening_sockets
195         listening_socket_handlers[sock]=handler
196
197 def input_thread(sock,addr):
198         print >>sys.stderr,"Connection from: %r" % (addr,)
199         localip=sock.getsockname()[0]
200         remoteip=addr[0]
201         buf=""
202         while 1:
203                 try:
204                         r=sock.recv(1024)
205                 except socket.error,e:
206                         if e.args[0]==errno.EINTR:
207                                 continue
208                 if not r:
209                         print >>sys.stderr,"No query"
210                         sock.close()
211                         return
212                 buf+=r
213                 if len(buf)>1024:
214                         print >>sys.stderr,"Query too long"
215                         sock.close()
216                         return
217                 if buf.find("\r\n")>=0:
218                         break
219         query=buf.split("\r",1)[0]
220         print >>sys.stderr,"Query: %r" % (query,)
221         sp=query.split(",")
222         if len(sp)!=2:
223                 print >>sys.stderr,"Invalid query"
224                 sock.close()
225                 return
226         local,remote=sp
227         try:
228                 local=int(local.strip())
229                 remote=int(remote.strip())
230         except ValueError:
231                 sock.send("%s,%s:ERROR:INVALID-PORT\r\n" % (local,remote))
232                 sock.close()
233                 print >>sys.stderr,"Invalid query"
234                 return
235         if local<1 or local>65535 or remote<1 or remote>65535:
236                 sock.send("%i,%i:ERROR:INVALID-PORT\r\n" % (local,remote))
237                 sock.close()
238                 print >>sys.stderr,"Invalid query"
239                 return
240         reply=None
241         mapping=None
242         for d in drivers:
243                 if isinstance(d,Mapping):
244                         mapping=d
245                         continue
246                 if not isinstance(d,Driver):
247                         continue
248                 r=d.lookup(local,localip,remote,remoteip)
249                 if r is None:
250                         continue
251                 if mapping and not r.startswith("ERROR:"):
252                         oldr=r
253                         r=mapping.lookup(r)
254                         print "%r -> %r" % (oldr,r)
255                 if r.startswith("ERROR:"):
256                         reply="%i,%i:%s" % (local,remote,r)
257                         break
258                 else:
259                         reply="%i,%i:USERID:UNIX:%s" % (local,remote,r)
260                         break
261         if not reply:
262                 reply="%i,%i:ERROR:NO-USER" % (local,remote)
263         print >>sys.stderr,"Reply: %s" % (reply,)
264         sock.send(reply+"\r\n")
265         sock.close()
266         return
267
268 def signal_handler(signum,frame):
269         global exit
270         exit=1
271         print >>sys.stderr,"Signal %i received, exiting." % (signum,)
272
273 def accept_connection(sock):
274         th=threading.Thread(target=input_thread,args=sock.accept())
275         th.setDaemon(1)
276         th.start()
277
278 def usage():
279         print "Simple Programmable Ident (RFC1413) Daemon"
280         print "(c) 2004 Jacek Konieczny"
281         print
282         print "Usage:"
283         print "    %s [options] [driver...]"
284         print
285         print "Options:"
286         print "    -h --help              display this help and exit."
287         print "    -i ADDR --ip=ADDR      bind to IP address ADDR."
288         print "    -p PORT --port=PORT    bind to port PORT (default: 113)."
289         print "    -u USER --user=USER    when started with uid=0, switch "
290         print "                           to user USER (default: 'nobody')"
291         print "    -p GROUP --group=GROUP when started with uid=0, switch "
292         print "                           to group GROUP (default: nobody's group)"
293         print "    --socketmode=MODE      access mode for listening socket (--socket driver)"
294         print "    --socketgroup=MODE     owner group for listening socket (--socket driver)"
295         print "Drivers:"
296         print "    --nouser               always reply with NO-USER error."
297         print "    --hidden               always reply with HIDDN-USER error."
298         print "    --fail                 always reply with UNKNOWN-ERROR error."
299         print "    --real                 reply with real connection user name (currently Linux"
300         print "                           only)."
301         print "    --map=FILE             user mapping file FILE for results of the following "
302         print "                           drivers"
303         print "    --static=USER          always reply with the same USER reply."
304         print "    --socket=PATH          listen on UNIX socket PATH for other servers"
305         print "                           registering their connections."
306
307
308 user="nobody"
309 group=None
310 ip="0.0.0.0"
311 port=113
312 drivers=[]
313 socketmode=0775
314 socketgroup=None
315
316 try:
317         opts,args=getopt.getopt(sys.argv[1:], "hi:p:u:g:", ["help","ip=","port=","user=","group=","map=","socketmode=","socketgroup=",
318                 "nouser","hidden","real","fail","static=","socket="])
319 except Exception,e:
320         print e
321         usage()
322         sys.exit(2)
323 for o,a in opts:
324         if o in ("-h","--help"):
325                 usage()
326                 sys.exit()
327         if o in ("-i","--ip"):
328                 ip=a
329         if o in ("-p","--port"):
330                 port=int(a)
331         if o in ("-u","--user"):
332                 user=a
333         if o in ("-g","--group"):
334                 group=a
335         if o in ("--socketmode",):
336                 socketmode=int(a,8)
337         if o in ("--socketgroup",):
338                 socketgroup=a
339         if o in ("--nouser",):
340                 drivers.append(NoUserDriver())
341         if o in ("--hidden",):
342                 drivers.append(HiddenUserDriver())
343         if o in ("--fail",):
344                 drivers.append(FailDriver())
345         if o in ("--real",):
346                 drivers.append(RealDriver())
347         if o in ("--map",):
348                 drivers.append(FileMapping(a))
349         if o in ("--static",):
350                 drivers.append(StaticDriver(a))
351         if o in ("--socket",):
352                 drivers.append(SocketDriver(a))
353 drivers.append(FailDriver())
354
355 if args:
356         usage()
357         sys.exit(2)
358
359 sock=socket.socket()
360 try:
361         sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
362 except:
363         pass
364 sock.bind((ip,port))
365
366 if os.getuid()==0:
367         pw=pwd.getpwnam(user)
368         uid=pw[2]
369         gid=pw[3]
370         if group:
371                 gr=grp.getgrnam(group)
372                 gid=gr[2]
373         os.setgroups([gid])
374         os.setgid(gid)
375         os.setuid(uid)
376
377 sock.listen(1)
378 buf=""
379 exit=0
380
381 register_listening_socket(sock,accept_connection)
382
383 signal.signal(signal.SIGINT,signal_handler)
384 signal.signal(signal.SIGPIPE,signal_handler)
385 signal.signal(signal.SIGTERM,signal_handler)
386
387 while not exit and listening_socket_handlers:
388         try:
389                 sockets=listening_socket_handlers.keys()
390                 id,od,ed=select.select(sockets,[],sockets,1)
391         except select.error,e:
392                 if e.args[0]==errno.EINTR:
393                         continue
394         for s in id:
395                 listening_socket_handlers[s](s)
396         for s in ed:
397                 print >>sys.stderr,"Error on socket: %r" % (s,)
398                 del listening_socket_handlers[s]
399 # vi: sts=4 et sw=4
400
Note: See TracBrowser for help on using the browser.