7f9617e55210fe5ff774c0e4ec0935ae228adae8
[lcore.git] / lsocket.pas
1 {lsocket.pas}\r
2 \r
3 {socket code by plugwash}\r
4 \r
5 { Copyright (C) 2005 Bas Steendijk and Peter Green\r
6   For conditions of distribution and use, see copyright notice in zlib_license.txt\r
7   which is included in the package\r
8   ----------------------------------------------------------------------------- }\r
9 {\r
10 changes by plugwash (20030728)\r
11 * created handlefdtrigger virtual method in tlasio (overridden in tlsocket) and moved a lot of code from messageloop into it\r
12 * changed tlasio to tlasio\r
13 * split fdhandle into fdhandlein and fdhandleout\r
14 * i now use fdsrmaster and fdswmaster instead of rebuilding the lists every loop\r
15 * split lsocket.pas into lsocket.pas and lcore.pas\r
16 \r
17 \r
18 changes by beware (20030903)\r
19 * added getxaddr, getxport (local addr, port, as string)\r
20 * added getpeername, remote addr+port as binary\r
21 * added htons and htonl functions (endian swap, same interface as windows API)\r
22 \r
23 beware (20030905)\r
24 * if connect failed (conn refused) set state to connected and call internalclose, to get closed handler (instead of fdclose)\r
25 * (lcore) if closing the fd's in internalcose, set fd's to -1 because closing an fd makes it invalid\r
26 \r
27 beware (20030927)\r
28 * fixed: on connect failed, tried to close fdhandle's which were already set to -1, added check\r
29 \r
30 beware (20031017)\r
31 * added getpeeraddr, getpeerport, remote addr+port as string\r
32 }\r
33 \r
34 \r
35 unit lsocket;\r
36 {$ifdef fpc}\r
37   {$mode delphi}\r
38 {$endif}\r
39 \r
40 {$include lcoreconfig.inc}\r
41 \r
42 interface\r
43   uses\r
44     sysutils,\r
45     {$ifdef win32}\r
46       windows,winsock,\r
47     {$else}\r
48 \r
49       {$ifdef VER1_0}\r
50         linux,\r
51       {$else}\r
52         baseunix,unix,unixutil,\r
53       {$endif}\r
54       sockets,\r
55     {$endif}\r
56     classes,{pgdebugout,}pgtypes,lcore,fd_utils,binipstuff,dnssync;\r
57 \r
58 {$ifdef ipv6}\r
59 const\r
60   v4listendefault:boolean=false;\r
61 {$endif}\r
62 \r
63 \r
64 type\r
65   sunB = packed record\r
66     s_b1, s_b2, s_b3, s_b4: byte;\r
67   end;\r
68 \r
69   SunW = packed record\r
70     s_w1, s_w2: word;\r
71   end;\r
72 \r
73   TInAddr = packed record\r
74     case integer of\r
75       0: (S_un_b: SunB);\r
76       1: (S_un_w: SunW);\r
77       2: (S_addr: cardinal);\r
78   end;\r
79 \r
80   type\r
81     TLsocket = class(tlasio)\r
82     public\r
83       //a: string;\r
84 \r
85       inAddr             : TInetSockAddrV;\r
86 \r
87       biniplist:tbiniplist;\r
88       trymoreips:boolean;\r
89       currentip:integer;\r
90       connecttimeout:tltimer;\r
91 \r
92 {      inAddrSize:integer;}\r
93 \r
94       //host               : THostentry      ;\r
95 \r
96       //mainthread         : boolean         ; //for debuggin only\r
97       addr:thostname;\r
98       port:ansistring;\r
99       localaddr:thostname;\r
100       localport:ansistring;\r
101       proto:ansistring;\r
102       udp,dgram:boolean;\r
103       listenqueue:integer;\r
104 \r
105       onconnecttryip:procedure(sender:tobject; const ip:tbinip) of object;\r
106 \r
107       {$ifdef secondlistener}\r
108       secondlistener:tlsocket;\r
109       lastsessionfromsecond:boolean;\r
110       procedure secondaccepthandler(sender:tobject;error:word);\r
111       procedure internalclose(error:word);override;\r
112       {$endif}\r
113       function getaddrsize:integer;\r
114       procedure connect; virtual;\r
115       procedure realconnect;\r
116       procedure bindsocket;\r
117       procedure listen;\r
118       function accept : longint;\r
119       function sendto(dest:TInetSockAddrV;destlen:integer;data:pointer;len:integer):integer; virtual;\r
120       function receivefrom(data:pointer;len:integer;var src:TInetSockAddrV;var srclen:integer):integer; virtual;\r
121 \r
122       procedure handlefdtrigger(readtrigger,writetrigger:boolean); override;\r
123       function send(data:pointer;len:integer):integer;override;\r
124       procedure sendstr(const str : tbufferstring);override;\r
125       function Receive(Buf:Pointer;BufSize:integer):integer; override;\r
126       function getpeername(var addr:tsockaddrin;addrlen:integer):integer; virtual;\r
127       procedure getXaddrbin(var binip:tbinip); virtual;\r
128       procedure getpeeraddrbin(var binip:tbinip); virtual;\r
129       function getXaddr:thostname; virtual;\r
130       function getpeeraddr:thostname; virtual;\r
131       function getXport:ansistring; virtual;\r
132       function getpeerport:ansistring; virtual;\r
133       constructor Create(AOwner: TComponent); override;\r
134 \r
135       //this one has to be kept public for now because lcorewsaasyncselect calls it\r
136       procedure connectionfailedhandler(error:word);\r
137     private\r
138       {$ifdef ipv6}\r
139         isv6socket : boolean; //identifies if the socket is v6, set by bindsocket\r
140       {$endif}\r
141       procedure taskcallconnectionfailedhandler(wparam,lparam : longint);\r
142 \r
143       procedure connecttimeouthandler(sender:tobject);\r
144       procedure connectsuccesshandler;\r
145       {$ifdef win32}\r
146         procedure myfdclose(fd : integer); override;\r
147         function myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt; override;\r
148         function myfdread(fd: LongInt;var buf;size: LongInt):LongInt; override;\r
149       {$endif}\r
150     end;\r
151     tsocket=longint; // for compatibility with twsocket\r
152 \r
153   twsocket=tlsocket; {easy}\r
154 \r
155 \r
156 const\r
157   TCP_NODELAY=1;\r
158   IPPROTO_TCP=6;\r
159 \r
160 implementation\r
161 {$include unixstuff.inc}\r
162 \r
163 \r
164 function tlsocket.getaddrsize:integer;\r
165 begin\r
166   result := inaddrsize(inaddr);\r
167 end;\r
168 \r
169 //I used to use the system versions of these from within lsocket (which has\r
170 //functions whose name clashes with them) by using sockets.* and but I can't do\r
171 //that anymore since in some cases connect is now provided by unixstuff.inc\r
172 //hence these wrapper functions --plugwash\r
173 {$ifndef win32}\r
174   function system_Connect(Sock: LongInt;const Addr;Addrlen: LongInt):Boolean;\r
175   begin\r
176     result := connect(sock,addr,addrlen);\r
177   end;\r
178   function system_SendTo(Sock: LongInt; const Buf;BufLen: LongInt;Flags: LongInt;var Addr;AddrLen: LongInt):LongInt;\r
179   begin\r
180     result := sendto(sock,buf,buflen,flags,addr,addrlen);\r
181   end;\r
182   function system_getpeername(Sock: LongInt;var Addr;var Addrlen: LongInt):LongInt;\r
183   begin\r
184     result := getpeername(sock,addr,addrlen);\r
185   end;\r
186   function system_listen(Sock: LongInt; MaxConnect: LongInt):Boolean;\r
187   begin\r
188     result := listen(sock,maxconnect);\r
189   end;\r
190   function system_Accept(Sock: LongInt;var Addr;var Addrlen: LongInt):LongInt;\r
191   begin\r
192     result := accept(sock,addr,addrlen);\r
193   end;\r
194 {$endif}\r
195 \r
196 procedure tlsocket.realconnect;\r
197 var\r
198   a,b:integer;\r
199   iptemp:tbinip;\r
200 begin\r
201   iptemp := biniplist_get(biniplist,currentip);\r
202   //writeln('trying to connect to ',ipbintostr(biniplist_get(biniplist,currentip)),'#',port);\r
203   if assigned(onconnecttryip) then onconnecttryip(self,iptemp);\r
204   makeinaddrv(iptemp,port,inaddr);\r
205   inc(currentip);\r
206   if (currentip >= biniplist_getcount(biniplist)) then trymoreips := false;\r
207 \r
208   udp := false;\r
209   if (uppercase(proto) = 'UDP') then begin\r
210     b := IPPROTO_UDP;\r
211     a := SOCK_DGRAM;\r
212     udp := true;\r
213     dgram := true;\r
214   end else if (uppercase(proto) = 'TCP') or (uppercase(proto) = '') then begin\r
215     b := IPPROTO_TCP;\r
216     a := SOCK_STREAM;\r
217     dgram := false;\r
218   end else if (uppercase(proto) = 'ICMP') or (strtointdef(proto,256) < 256) then begin\r
219     b := strtointdef(proto,IPPROTO_ICMP);\r
220     a := SOCK_RAW;\r
221     dgram := true;\r
222   end else begin\r
223     raise ESocketException.create('unrecognised protocol');\r
224   end;\r
225 \r
226   a := Socket(inaddr.inaddr.family,a,b);\r
227   //writeln(ord(inaddr.inaddr.family));\r
228   if a = -1 then begin\r
229     //unable to create socket, fire an error event (better to use an error event\r
230     //to avoid poor interaction with multilistener stuff.\r
231     //a socket value of -2 is a special value to say there is no socket but\r
232     //we want internalclose to act as if there was\r
233     fdhandlein := -2;\r
234     fdhandleout := -2;\r
235     tltask.create(taskcallconnectionfailedhandler,self,{$ifdef win32}wsagetlasterror{$else}socketerror{$endif},0);\r
236     exit;\r
237   end;\r
238   try\r
239     dup(a);\r
240     bindsocket;\r
241     if dgram then begin\r
242       {$ifndef win32}\r
243         SetSocketOptions(fdhandleout, SOL_SOCKET, SO_BROADCAST, 'TRUE', Length('TRUE'));\r
244       {$else}\r
245         SetSockOpt(fdhandleout, SOL_SOCKET, SO_BROADCAST, 'TRUE', Length('TRUE'));\r
246       {$endif}\r
247       state := wsconnected;\r
248       if assigned(onsessionconnected) then onsessionconnected(self,0);\r
249 \r
250       eventcore.rmasterset(fdhandlein,false);\r
251       eventcore.wmasterclr(fdhandleout);\r
252     end else begin\r
253       state :=wsconnecting;\r
254       {$ifdef win32}\r
255         //writeln(inaddr.inaddr.port);\r
256         winsock.Connect(fdhandlein,winsock.psockaddr(@inADDR)^,getaddrsize);\r
257       {$else}\r
258         system_Connect(fdhandlein,inADDR,getaddrsize);\r
259       {$endif}\r
260       eventcore.rmasterset(fdhandlein,false);\r
261       eventcore.wmasterset(fdhandleout);\r
262       if trymoreips then connecttimeout.enabled := true;\r
263     end;\r
264     //sendq := '';\r
265   except\r
266     on e: exception do begin\r
267       fdcleanup;\r
268       raise; //reraise the exception\r
269     end;\r
270   end;\r
271 \r
272 end;\r
273 \r
274 procedure tlsocket.connecttimeouthandler(sender:tobject);\r
275 begin\r
276   connecttimeout.enabled := false;\r
277   destroying := true; //hack to not cause handler to trigger\r
278   internalclose(0);\r
279   destroying := false;\r
280   realconnect;\r
281 end;\r
282 \r
283 \r
284 \r
285 \r
286 procedure tlsocket.connect;\r
287 var\r
288   a:integer;\r
289   ip:tbinip;\r
290 begin\r
291   if state <> wsclosed then close;\r
292   //prevtime := 0;\r
293   if isbiniplist(addr) then biniplist := addr else biniplist := forwardlookuplist(addr,0);\r
294   if biniplist_getcount(biniplist) = 0 then raise exception.create('unable to resolve '+addr);\r
295 \r
296   //makeinaddrv(addr,port,inaddr);\r
297 \r
298   currentip := 0;\r
299   if not assigned(connecttimeout) then begin\r
300     connecttimeout := tltimer.create(self);\r
301     connecttimeout.ontimer := connecttimeouthandler;\r
302     connecttimeout.interval := 2500;\r
303     connecttimeout.enabled := false;\r
304   end;\r
305   realconnect;\r
306 end;\r
307 \r
308 procedure tlsocket.sendstr(const str : tbufferstring);\r
309 begin\r
310   if dgram then begin\r
311     send(@str[1],length(str))\r
312   end else begin\r
313     inherited sendstr(str);\r
314   end;\r
315 end;\r
316 \r
317 function tlsocket.send(data:pointer;len:integer):integer;\r
318 begin\r
319   if dgram then begin\r
320 //    writeln('sending to '+ipbintostr(inaddrvtobinip(inaddr)),' ',htons(inaddr.inaddr.port),' ',len,' bytes');\r
321     result := sendto(inaddr,getaddrsize,data,len);\r
322 \r
323 //    writeln('send result ',result);\r
324 //    writeln('errno',errno);\r
325   end else begin\r
326     result := inherited send(data,len);\r
327   end;\r
328 end;\r
329 \r
330 \r
331 function tlsocket.receive(Buf:Pointer;BufSize:integer):integer;\r
332 begin\r
333   if dgram then begin\r
334     {$ifdef secondlistener}\r
335     if lastsessionfromsecond then begin\r
336       result := secondlistener.receive(buf,bufsize);\r
337       lastsessionfromsecond := false;\r
338     end else\r
339     {$endif}\r
340       result := myfdread(self.fdhandlein,buf^,bufsize);\r
341   end else begin\r
342     result := inherited receive(buf,bufsize);\r
343   end;\r
344 end;\r
345 \r
346 procedure tlsocket.bindsocket;\r
347 var\r
348   a:integer;\r
349   inAddrtemp:TInetSockAddrV;\r
350   inAddrtempx:{$ifdef win32}winsock.TSockaddr{$else}TInetSockAddrV{$endif} absolute inaddrtemp;\r
351   inaddrtempsize:integer;\r
352 begin\r
353   try\r
354     if (localaddr <> '') or (localport <> '') then begin\r
355       if localaddr = '' then begin\r
356         {$ifdef ipv6}\r
357         if inaddr.inaddr.family = AF_INET6 then localaddr := '::' else\r
358         {$endif}\r
359         localaddr := '0.0.0.0';\r
360       end;\r
361       //gethostbyname(localaddr,host);\r
362       inaddrtempsize := makeinaddrv(forwardlookup(localaddr,0),localport,inaddrtemp);\r
363       {$ifdef ipv6}\r
364         isv6socket := (inaddrtemp.inaddr.family = AF_INET6);\r
365       {$endif}\r
366       If Bind(fdhandlein,inaddrtempx,inaddrtempsize)<> {$ifdef win32}0{$else}true{$endif} Then begin\r
367         state := wsclosed;\r
368         lasterror := {$ifdef win32}getlasterror{$else}socketerror{$endif};\r
369         raise ESocketException.create('unable to bind on address '+localaddr+'#'+localport+', error '+inttostr(lasterror));\r
370       end;\r
371       state := wsbound;\r
372     end;\r
373   except\r
374     on e: exception do begin\r
375       fdcleanup;\r
376       raise; //reraise the exception\r
377     end;\r
378   end;\r
379 end;\r
380 \r
381 procedure tlsocket.listen;\r
382 var\r
383   yes,no:longint;\r
384   socktype:integer;\r
385   biniptemp:tbinip;\r
386   origaddr:thostname;\r
387 begin\r
388   if state <> wsclosed then close;\r
389   udp := uppercase(proto) = 'UDP';\r
390   if udp then begin\r
391     socktype := SOCK_DGRAM;\r
392     dgram := true;\r
393   end else socktype := SOCK_STREAM;\r
394   origaddr := addr;\r
395 \r
396   if addr = '' then begin\r
397     {$ifdef ipv6}\r
398     //writeln('ipv6 is defined');\r
399     if not v4listendefault then begin\r
400       //writeln('setting addr to ::');\r
401       addr := '::';\r
402     end else\r
403     {$endif}\r
404     addr := '0.0.0.0';\r
405   end;\r
406   if isbiniplist(addr) then biniptemp := biniplist_get(addr,0) else biniptemp := forwardlookup(addr,10);\r
407   addr := ipbintostr(biniptemp);\r
408   //writeln('after ipbintostr call addr =',addr);\r
409   //writeln('biniptemp.family =',biniptemp.family);\r
410   //writeln('AF_INET6=',AF_INET6);\r
411   //writeln('PF_INET6=',PF_INET6);\r
412   //writeln('AF_INET=',AF_INET);\r
413   //writeln('PF_INET=',PF_INET);\r
414   \r
415   fdhandlein := socket(biniptemp.family,socktype,0);\r
416   {$ifdef ipv6}\r
417   if (addr = '::') and (origaddr = '') and (fdhandlein < 0) then begin\r
418     {writeln('failed to create an IPV6 socket with error ',socketerror,'. trying to create an IPV4 one instead');}\r
419     addr := '0.0.0.0';\r
420     fdhandlein := socket(PF_INET,socktype,0);\r
421   end;\r
422   {$endif}\r
423 \r
424   if fdhandlein = -1 then raise ESocketException.create('unable to create socket'{$ifdef win32}+' error='+inttostr(wsagetlasterror){$endif});\r
425   dupnowatch(fdhandlein); // sets up maxs and copies handle to fdhandleout among other things\r
426   //eventcore.setfdreverse(fdhandlein,self); //already taken care of by dup\r
427   state := wsclosed; // then set this back as it was an undesired side effect of dup\r
428 \r
429   try\r
430     yes := $01010101;  {Copied this from existing code. Value is empiric,\r
431                     but works. (yes=true<>0) }\r
432     no := 0;\r
433     {$ifndef win32}\r
434       if SetSocketOptions(fdhandlein, SOL_SOCKET, SO_REUSEADDR,yes,sizeof(yes))=-1 then begin\r
435         raise ESocketException.create('unable to set SO_REUSEADDR socket option');\r
436       end;\r
437       //writeln('addr=',addr);\r
438       //writeln('setting IPV6_V6ONLY option to 0');\r
439       //allow v4 connections on v6 sockets\r
440       if biniptemp.family = af_inet6 then begin\r
441         if SetSocketOptions(fdhandlein, IPPROTO_IPV6,IPV6_V6ONLY,no,sizeof(no))=-1 then begin\r
442           writeln(IPPROTO_IPV6);\r
443           writeln(IPV6_V6ONLY);\r
444           raise ESocketException.create('unable to set IPV6_V6ONLY socket option error='+inttostr(socketerror));\r
445           \r
446         end;\r
447       end;\r
448     {$endif}\r
449     localaddr := addr;\r
450     localport := port;\r
451     bindsocket;\r
452 \r
453     if not udp then begin\r
454       {!!! allow custom queue length? default 5}\r
455       if listenqueue = 0 then listenqueue := 5;\r
456       If {$ifdef win32}winsock.listen{$else}system_listen{$endif}(fdhandlein,listenqueue)<>{$ifdef win32}0{$else}true{$endif} Then raise\r
457 esocketexception.create('unable to listen');\r
458       state := wsListening;\r
459     end else begin\r
460       {$ifndef win32}\r
461         SetSocketOptions(fdhandleout, SOL_SOCKET, SO_BROADCAST, 'TRUE', Length('TRUE'));\r
462       {$else}\r
463         SetSockOpt(fdhandleout, SOL_SOCKET, SO_BROADCAST, 'TRUE', Length('TRUE'));\r
464       {$endif}\r
465       state := wsconnected;\r
466     end;\r
467 \r
468     {$ifdef secondlistener}\r
469     //listening on ::. try to listen on 0.0.0.0 as well for platforms which don't already do that\r
470     if addr = '::' then begin\r
471       secondlistener := tlsocket.create(nil);\r
472       secondlistener.proto := proto;\r
473       secondlistener.addr := '0.0.0.0';\r
474       secondlistener.port := port;\r
475       if udp then begin\r
476         secondlistener.ondataavailable := secondaccepthandler;\r
477       end else begin\r
478         secondlistener.onsessionAvailable := secondaccepthandler;\r
479       end;\r
480       try\r
481         secondlistener.listen;\r
482       except\r
483         secondlistener.destroy;\r
484         secondlistener := nil;\r
485       end;\r
486     end;\r
487     {$endif}\r
488   finally\r
489     if state = wsclosed then begin\r
490       if fdhandlein >= 0 then begin\r
491         {one *can* get here without fd -beware}\r
492         eventcore.rmasterclr(fdhandlein);\r
493         myfdclose(fdhandlein); // we musnt leak file discriptors\r
494         eventcore.setfdreverse(fdhandlein,nil);\r
495         fdhandlein := -1;\r
496       end;\r
497     end else begin\r
498       eventcore.rmasterset(fdhandlein,not udp);\r
499     end;\r
500     if fdhandleout >= 0 then eventcore.wmasterclr(fdhandleout);\r
501   end;\r
502   //writeln('listened on addr '+addr+':'+port+' '+proto+' using socket number ',fdhandlein);\r
503 end;\r
504 \r
505 {$ifdef secondlistener}\r
506 procedure tlsocket.internalclose(error:word);\r
507 begin\r
508   if assigned(secondlistener) then begin\r
509     secondlistener.destroy;\r
510     secondlistener := nil;\r
511   end;\r
512   inherited internalclose(error);\r
513 end;\r
514 \r
515 procedure tlsocket.secondaccepthandler;\r
516 begin\r
517   lastsessionfromsecond := true;\r
518   if udp then begin\r
519     ondataavailable(self,error);\r
520   end else begin\r
521     if assigned(onsessionavailable) then onsessionavailable(self,error);\r
522   end;\r
523 end;\r
524 {$endif}\r
525 \r
526 function tlsocket.accept : longint;\r
527 var\r
528   FromAddrSize     : LongInt;        // i don't realy know what to do with these at this\r
529   FromAddr         : TInetSockAddrV;  // at this point time will tell :)\r
530   a:integer;\r
531 begin\r
532   {$ifdef secondlistener}\r
533   if (lastsessionfromsecond) then begin\r
534     lastsessionfromsecond := false;\r
535     result := secondlistener.accept;\r
536     exit;\r
537   end;\r
538   {$endif}\r
539 \r
540   FromAddrSize := Sizeof(FromAddr);\r
541   {$ifdef win32}\r
542     result := winsock.accept(fdhandlein,@fromaddr,@fromaddrsize);\r
543   {$else}\r
544     result := system_accept(fdhandlein,fromaddr,fromaddrsize);\r
545   {$endif}\r
546   //now we have accepted one request start monitoring for more again\r
547   eventcore.rmasterset(fdhandlein,true);\r
548 \r
549   if result = -1 then begin\r
550     raise esocketexception.create('error '+inttostr({$ifdef win32}getlasterror{$else}socketerror{$endif})+' while accepting');\r
551   end;\r
552   if result > absoloutemaxs then begin\r
553     myfdclose(result);\r
554     a := result;\r
555     result := -1;\r
556     raise esocketexception.create('file discriptor out of range: '+inttostr(a));\r
557   end;\r
558 end;\r
559 \r
560 \r
561 function tlsocket.sendto(dest:TInetSockAddrV;destlen:integer;data:pointer;len:integer):integer;\r
562 var\r
563   {$ifdef ipv6}\r
564     realdest : tinetsockaddrv;\r
565     biniptemp : tbinip;\r
566   {$endif}\r
567   destx : {$ifdef win32}winsock.pSockAddr{$else}pInetSockAddrV{$endif};\r
568 \r
569 begin\r
570   {$ifdef secondlistener}\r
571     if assigned(secondlistener) then if (dest.inaddr.family = AF_INET) then begin\r
572       result := secondlistener.sendto(dest,destlen,data,len);\r
573       exit;\r
574     end;\r
575   {$endif}\r
576   {$ifdef ipv6}\r
577     if isv6socket then begin\r
578       biniptemp := inaddrvtobinip(dest);\r
579       converttov6(biniptemp);\r
580       destlen := makeinaddrv(biniptemp,inttostr(ntohs(dest.InAddr.port)),realdest);\r
581       destx := {$ifdef win32}winsock.pSockAddr{$else}pInetSockAddrV{$endif}(@realdest)\r
582     end else begin\r
583       destx := {$ifdef win32}winsock.pSockAddr{$else}pInetSockAddrV{$endif}(@dest)\r
584     end;\r
585   {$else}\r
586     destx := {$ifdef win32}winsock.pSockAddr{$else}pInetSockAddrV{$endif}(@dest)\r
587   {$endif}\r
588 \r
589   result := {$ifdef win32}winsock.sendto{$else}system_sendto{$endif}(self.fdhandleout,data^,len,0,destx^,destlen);\r
590 end;\r
591 \r
592 \r
593 function tlsocket.receivefrom(data:pointer;len:integer;var src:TInetSockAddrV;var srclen:integer):integer;\r
594 var\r
595   tempsrc:TInetSockAddrV;\r
596   tempsrclen:integer;\r
597   srcx : {$ifdef win32}winsock.TSockAddr{$else}TInetSockAddrV{$endif} absolute tempsrc;\r
598   biniptemp:tbinip;\r
599 begin\r
600   {$ifdef secondlistener}\r
601   if assigned(secondlistener) then if lastsessionfromsecond then begin\r
602     lastsessionfromsecond := false;\r
603     result := secondlistener.receivefrom(data,len,src,srclen);\r
604     exit;\r
605   end;\r
606   {$endif}\r
607   tempsrclen := sizeof(tempsrc);\r
608   result := recvfrom(self.fdhandlein,data^,len,0,srcx,tempsrclen);\r
609 \r
610   {$ifdef ipv6}\r
611   biniptemp := inaddrvtobinip(tempsrc);\r
612   if needconverttov4(biniptemp) then begin\r
613     converttov4(biniptemp);\r
614     tempsrclen := makeinaddrv(biniptemp,inttostr(ntohs(tempsrc.InAddr.port)),tempsrc);\r
615   end;\r
616   {$endif}\r
617 \r
618   move(tempsrc,src,srclen);\r
619   srclen := tempsrclen;\r
620 end;\r
621 \r
622 procedure tlsocket.taskcallconnectionfailedhandler(wparam,lparam : longint);\r
623 begin\r
624   connectionfailedhandler(wparam);\r
625 end;\r
626 \r
627 procedure tlsocket.connectionfailedhandler(error:word);\r
628 begin\r
629    if trymoreips then begin\r
630 //     writeln('failed with error ',error);\r
631      connecttimeout.enabled := false;\r
632      destroying := true;\r
633      state := wsconnected;\r
634      self.internalclose(0);\r
635      destroying := false;\r
636      realconnect;\r
637    end else begin\r
638      state := wsconnected;\r
639      if assigned(onsessionconnected) then onsessionconnected(self,error);\r
640      self.internalclose(0);\r
641      recvq.del(maxlongint);\r
642    end;\r
643 end;\r
644 \r
645 procedure tlsocket.connectsuccesshandler;\r
646 begin\r
647    trymoreips := false;\r
648    connecttimeout.enabled := false;\r
649    if assigned(onsessionconnected) then onsessionconnected(self,0);\r
650 end;\r
651 \r
652 \r
653 procedure tlsocket.handlefdtrigger(readtrigger,writetrigger:boolean);\r
654 var\r
655   tempbuf:array[0..receivebufsize-1] of byte;\r
656 begin\r
657 //  writeln('got a fd trigger, readtrigger=',readtrigger,' writetrigger=',writetrigger,' state=',integer(state));\r
658   if (state =wslistening) and readtrigger then begin\r
659 {    debugout('listening socket triggered on read');}\r
660     eventcore.rmasterclr(fdhandlein);\r
661     if assigned(onsessionAvailable) then onsessionAvailable(self,0);\r
662   end;\r
663   if dgram and readtrigger then begin\r
664     if assigned(ondataAvailable) then ondataAvailable(self,0);\r
665     {!!!test}\r
666     exit;\r
667   end;\r
668   if (state =wsconnecting) and writetrigger then begin\r
669     // code for dealing with the reults of a non-blocking connect is\r
670     // rather complex\r
671     // if just write is triggered it means connect suceeded\r
672     // if both read and write are triggered it can mean 2 things\r
673     // 1: connect ok and data availible\r
674     // 2: connect fail\r
675     // to find out which you must read from the socket and look for errors\r
676     // there if we read successfully we drop through into the code for fireing\r
677     // the read event\r
678     if not readtrigger then begin\r
679       state := wsconnected;\r
680       connectsuccesshandler;\r
681     end else begin\r
682       numread := myfdread(fdhandlein,tempbuf,sizeof(tempbuf));\r
683       if numread <> -1 then begin\r
684         state := wsconnected;\r
685         connectsuccesshandler;\r
686         //connectread := true;\r
687         recvq.add(@tempbuf,numread);\r
688       end else begin\r
689         connectionfailedhandler({$ifdef win32}wsagetlasterror{$else}linuxerror{$endif});\r
690         exit;\r
691       end;\r
692       // if things went well here we are now in the state wsconnected with data sitting in our receive buffer\r
693       // so we drop down into the processing for data availible\r
694     end;\r
695     if fdhandlein >= 0 then begin\r
696       if state = wsconnected then begin\r
697         eventcore.rmasterset(fdhandlein,false);\r
698       end else begin\r
699         eventcore.rmasterclr(fdhandlein);\r
700       end;\r
701     end;\r
702     if fdhandleout >= 0 then begin\r
703       if sendq.size = 0 then begin\r
704         //don't clear the bit in fdswmaster if data is in the sendq\r
705         eventcore.wmasterclr(fdhandleout);\r
706       end;\r
707     end;\r
708 \r
709   end;\r
710   inherited handlefdtrigger(readtrigger,writetrigger);\r
711 end;\r
712 \r
713 constructor tlsocket.Create(AOwner: TComponent);\r
714 begin\r
715   inherited create(aowner);\r
716   closehandles := true;\r
717   trymoreips := true;\r
718 end;\r
719 \r
720 \r
721 \r
722 function tlsocket.getpeername(var addr:tsockaddrin;addrlen:integer):integer;\r
723 var\r
724   addrx : {$ifdef win32}winsock.tsockaddr{$else}tsockaddrin{$endif} absolute addr;\r
725 begin\r
726   result := {$ifdef win32}winsock.getpeername{$else}system_getpeername{$endif}(self.fdhandlein,addrx,addrlen);\r
727 end;\r
728 \r
729 procedure tlsocket.getxaddrbin(var binip:tbinip);\r
730 var\r
731   addr:tinetsockaddrv;\r
732   i:integer;\r
733 begin\r
734   i := sizeof(addr);\r
735   fillchar(addr,sizeof(addr),0);\r
736 \r
737   {$ifdef win32}\r
738     winsock.getsockname(self.fdhandlein,psockaddr(@addr)^,i);\r
739   {$else}\r
740     getsocketname(self.fdhandlein,addr,i);\r
741   {$endif}\r
742   binip := inaddrvtobinip(addr);\r
743   converttov4(binip);\r
744 end;\r
745 \r
746 procedure tlsocket.getpeeraddrbin(var binip:tbinip);\r
747 var\r
748   addr:tinetsockaddrv;\r
749   i:integer;\r
750 begin\r
751   i := sizeof(addr);\r
752   fillchar(addr,sizeof(addr),0);\r
753   {$ifdef win32}\r
754     winsock.getpeername(self.fdhandlein,psockaddr(@addr)^,i);\r
755   {$else}\r
756     system_getpeername(self.fdhandlein,addr,i);\r
757   {$endif}\r
758 \r
759   binip := inaddrvtobinip(addr);\r
760   converttov4(binip);\r
761 end;\r
762 \r
763 function tlsocket.getXaddr:thostname;\r
764 var\r
765   biniptemp:tbinip;\r
766 begin\r
767   getxaddrbin(biniptemp);\r
768   result := ipbintostr(biniptemp);\r
769   if result = '' then result := 'error';\r
770 end;\r
771 \r
772 function tlsocket.getpeeraddr:thostname;\r
773 var\r
774   biniptemp:tbinip;\r
775 begin\r
776   getpeeraddrbin(biniptemp);\r
777   result := ipbintostr(biniptemp);\r
778   if result = '' then result := 'error';\r
779 end;\r
780 \r
781 function tlsocket.getXport:ansistring;\r
782 var\r
783   addr:tinetsockaddrv;\r
784   i:integer;\r
785 begin\r
786   i := sizeof(addr);\r
787   {$ifdef win32}\r
788     winsock.getsockname(self.fdhandlein,psockaddrin(@addr)^,i);\r
789 \r
790   {$else}\r
791     getsocketname(self.fdhandlein,addr,i);\r
792 \r
793   {$endif}\r
794   result := inttostr(htons(addr.InAddr.port));\r
795 end;\r
796 \r
797 function tlsocket.getpeerport:ansistring;\r
798 var\r
799   addr:tinetsockaddrv;\r
800   i:integer;\r
801 begin\r
802   i := sizeof(addr);\r
803   {$ifdef win32}\r
804     winsock.getpeername(self.fdhandlein,psockaddrin(@addr)^,i);\r
805 \r
806   {$else}\r
807     system_getpeername(self.fdhandlein,addr,i);\r
808 \r
809   {$endif}\r
810   result := inttostr(htons(addr.InAddr.port));\r
811 end;\r
812 \r
813 {$ifdef win32}\r
814   procedure tlsocket.myfdclose(fd : integer);\r
815   begin\r
816     closesocket(fd);\r
817   end;\r
818   function tlsocket.myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt;\r
819   begin\r
820     result := winsock.send(fd,(@buf)^,size,0);\r
821   end;\r
822   function tlsocket.myfdread(fd: LongInt;var buf;size: LongInt):LongInt;\r
823   begin\r
824     result := winsock.recv(fd,buf,size,0);\r
825   end;\r
826 {$endif}\r
827 \r
828 end.\r
829 \r