line endings related fixes
[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 internalclose, set fds 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 mswindows}\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 debugging 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 \r
138       {public in tlasio, and can't be private in both places, so should be public here. \r
139       fixes delphi warning --beware}\r
140       {$ifdef mswindows}\r
141         procedure myfdclose(fd : integer); override;\r
142         function myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt; override;\r
143         function myfdread(fd: LongInt;var buf;size: LongInt):LongInt; override;\r
144       {$endif}\r
145 \r
146     private\r
147       {$ifdef ipv6}\r
148         isv6socket : boolean; //identifies if the socket is v6, set by bindsocket\r
149       {$endif}\r
150       procedure taskcallconnectionfailedhandler(wparam,lparam : longint);\r
151 \r
152       procedure connecttimeouthandler(sender:tobject);\r
153       procedure connectsuccesshandler;\r
154     end;\r
155     tsocket=longint; // for compatibility with twsocket\r
156 \r
157   twsocket=tlsocket; {easy}\r
158 \r
159 \r
160 const\r
161   TCP_NODELAY=1;\r
162   IPPROTO_TCP=6;\r
163 \r
164 implementation\r
165 {$include unixstuff.inc}\r
166 \r
167 \r
168 function tlsocket.getaddrsize:integer;\r
169 begin\r
170   result := inaddrsize(inaddr);\r
171 end;\r
172 \r
173 //I used to use the system versions of these from within lsocket (which has\r
174 //functions whose name clashes with them) by using sockets.* and but I can't do\r
175 //that anymore since in some cases connect is now provided by unixstuff.inc\r
176 //hence these wrapper functions --plugwash\r
177 {$ifndef mswindows}\r
178   function system_Connect(Sock: LongInt;const Addr;Addrlen: LongInt):Boolean;\r
179   begin\r
180     result := connect(sock,addr,addrlen);\r
181   end;\r
182   function system_SendTo(Sock: LongInt; const Buf;BufLen: LongInt;Flags: LongInt;var Addr;AddrLen: LongInt):LongInt;\r
183   begin\r
184     result := sendto(sock,buf,buflen,flags,addr,addrlen);\r
185   end;\r
186   function system_getpeername(Sock: LongInt;var Addr;var Addrlen: LongInt):LongInt;\r
187   begin\r
188     result := getpeername(sock,addr,addrlen);\r
189   end;\r
190   function system_listen(Sock: LongInt; MaxConnect: LongInt):Boolean;\r
191   begin\r
192     result := listen(sock,maxconnect);\r
193   end;\r
194   function system_Accept(Sock: LongInt;var Addr;var Addrlen: LongInt):LongInt;\r
195   begin\r
196     result := accept(sock,addr,addrlen);\r
197   end;\r
198 {$endif}\r
199 \r
200 procedure tlsocket.realconnect;\r
201 var\r
202   a,b:integer;\r
203   iptemp:tbinip;\r
204 begin\r
205   iptemp := biniplist_get(biniplist,currentip);\r
206   //writeln('trying to connect to ',ipbintostr(biniplist_get(biniplist,currentip)),'#',port);\r
207   if assigned(onconnecttryip) then onconnecttryip(self,iptemp);\r
208   makeinaddrv(iptemp,port,inaddr);\r
209   inc(currentip);\r
210   if (currentip >= biniplist_getcount(biniplist)) then trymoreips := false;\r
211 \r
212   udp := false;\r
213   if (uppercase(proto) = 'UDP') then begin\r
214     b := IPPROTO_UDP;\r
215     a := SOCK_DGRAM;\r
216     udp := true;\r
217     dgram := true;\r
218   end else if (uppercase(proto) = 'TCP') or (uppercase(proto) = '') then begin\r
219     b := IPPROTO_TCP;\r
220     a := SOCK_STREAM;\r
221     dgram := false;\r
222   end else if (uppercase(proto) = 'ICMP') or (strtointdef(proto,256) < 256) then begin\r
223     b := strtointdef(proto,IPPROTO_ICMP);\r
224     a := SOCK_RAW;\r
225     dgram := true;\r
226   end else begin\r
227     raise ESocketException.create('unrecognised protocol');\r
228   end;\r
229 \r
230   a := Socket(inaddr.inaddr.family,a,b);\r
231   //writeln(ord(inaddr.inaddr.family));\r
232   if a = -1 then begin\r
233     //unable to create socket, fire an error event (better to use an error event\r
234     //to avoid poor interaction with multilistener stuff.\r
235     //a socket value of -2 is a special value to say there is no socket but\r
236     //we want internalclose to act as if there was\r
237     fdhandlein := -2;\r
238     fdhandleout := -2;\r
239     tltask.create(taskcallconnectionfailedhandler,self,{$ifdef mswindows}wsagetlasterror{$else}socketerror{$endif},0);\r
240     exit;\r
241   end;\r
242   try\r
243     dup(a);\r
244     bindsocket;\r
245     if dgram then begin\r
246       {$ifndef mswindows}\r
247         SetSocketOptions(fdhandleout, SOL_SOCKET, SO_BROADCAST, 'TRUE', Length('TRUE'));\r
248       {$else}\r
249         SetSockOpt(fdhandleout, SOL_SOCKET, SO_BROADCAST, 'TRUE', Length('TRUE'));\r
250       {$endif}\r
251       state := wsconnected;\r
252       if assigned(onsessionconnected) then onsessionconnected(self,0);\r
253 \r
254       eventcore.rmasterset(fdhandlein,false);\r
255       eventcore.wmasterclr(fdhandleout);\r
256     end else begin\r
257       state :=wsconnecting;\r
258       {$ifdef mswindows}\r
259         //beware: atleast on windows, wsaasyncselect set interest in events before connecting, or a connect error isn't seen if it happens immediately during connect\r
260         eventcore.rmasterset(fdhandlein,false);\r
261         eventcore.wmasterset(fdhandleout);\r
262         //writeln(inaddr.inaddr.port);\r
263         winsock.Connect(fdhandlein,winsock.psockaddr(@inADDR)^,getaddrsize);\r
264       {$else}\r
265         system_Connect(fdhandlein,inADDR,getaddrsize);\r
266         eventcore.rmasterset(fdhandlein,false);\r
267         eventcore.wmasterset(fdhandleout);\r
268       {$endif}\r
269       if trymoreips then connecttimeout.enabled := true;\r
270     end;\r
271     //sendq := '';\r
272   except\r
273     on e: exception do begin\r
274       fdcleanup;\r
275       raise; //reraise the exception\r
276     end;\r
277   end;\r
278 \r
279 end;\r
280 \r
281 procedure tlsocket.connecttimeouthandler(sender:tobject);\r
282 begin\r
283   connecttimeout.enabled := false;\r
284   destroying := true; //hack to not cause handler to trigger\r
285   internalclose(0);\r
286   destroying := false;\r
287   realconnect;\r
288 end;\r
289 \r
290 \r
291 \r
292 \r
293 procedure tlsocket.connect;\r
294 begin\r
295   if state <> wsclosed then close;\r
296   //prevtime := 0;\r
297   if isbiniplist(addr) then biniplist := addr else biniplist := forwardlookuplist(addr,0);\r
298   if biniplist_getcount(biniplist) = 0 then raise exception.create('unable to resolve '+addr);\r
299 \r
300   //makeinaddrv(addr,port,inaddr);\r
301 \r
302   currentip := 0;\r
303   if not assigned(connecttimeout) then begin\r
304     connecttimeout := tltimer.create(self);\r
305     connecttimeout.ontimer := connecttimeouthandler;\r
306     connecttimeout.interval := 5000;\r
307     connecttimeout.enabled := false;\r
308   end;\r
309   realconnect;\r
310 end;\r
311 \r
312 procedure tlsocket.sendstr(const str : tbufferstring);\r
313 begin\r
314   if dgram then begin\r
315     send(@str[1],length(str))\r
316   end else begin\r
317     inherited sendstr(str);\r
318   end;\r
319 end;\r
320 \r
321 function tlsocket.send(data:pointer;len:integer):integer;\r
322 begin\r
323   if dgram then begin\r
324 //    writeln('sending to '+ipbintostr(inaddrvtobinip(inaddr)),' ',htons(inaddr.inaddr.port),' ',len,' bytes');\r
325     result := sendto(inaddr,getaddrsize,data,len);\r
326 \r
327 //    writeln('send result ',result);\r
328 //    writeln('errno',errno);\r
329   end else begin\r
330     result := inherited send(data,len);\r
331   end;\r
332 end;\r
333 \r
334 \r
335 function tlsocket.receive(Buf:Pointer;BufSize:integer):integer;\r
336 begin\r
337   if dgram then begin\r
338     {$ifdef secondlistener}\r
339     if lastsessionfromsecond then begin\r
340       result := secondlistener.receive(buf,bufsize);\r
341       lastsessionfromsecond := false;\r
342     end else\r
343     {$endif}\r
344       result := myfdread(self.fdhandlein,buf^,bufsize);\r
345   end else begin\r
346     result := inherited receive(buf,bufsize);\r
347   end;\r
348 end;\r
349 \r
350 procedure tlsocket.bindsocket;\r
351 var\r
352   inAddrtemp:TInetSockAddrV;\r
353   inAddrtempx:{$ifdef mswindows}winsock.TSockaddr{$else}TInetSockAddrV{$endif} absolute inaddrtemp;\r
354   inaddrtempsize:integer;\r
355 begin\r
356   try\r
357     if (localaddr <> '') or (localport <> '') then begin\r
358       if localaddr = '' then begin\r
359         {$ifdef ipv6}\r
360         if inaddr.inaddr.family = AF_INET6 then localaddr := '::' else\r
361         {$endif}\r
362         localaddr := '0.0.0.0';\r
363       end;\r
364       //gethostbyname(localaddr,host);\r
365       inaddrtempsize := makeinaddrv(forwardlookup(localaddr,0),localport,inaddrtemp);\r
366       {$ifdef ipv6}\r
367         isv6socket := (inaddrtemp.inaddr.family = AF_INET6);\r
368       {$endif}\r
369       If Bind(fdhandlein,inaddrtempx,inaddrtempsize)<> {$ifdef mswindows}0{$else}true{$endif} Then begin\r
370         state := wsclosed;\r
371         lasterror := {$ifdef mswindows}getlasterror{$else}socketerror{$endif};\r
372         raise ESocketException.create('unable to bind on address '+localaddr+'#'+localport+', error '+inttostr(lasterror));\r
373       end;\r
374       state := wsbound;\r
375     end;\r
376   except\r
377     on e: exception do begin\r
378       fdcleanup;\r
379       raise; //reraise the exception\r
380     end;\r
381   end;\r
382 end;\r
383 \r
384 procedure tlsocket.listen;\r
385 var\r
386   {$ifndef mswindows}\r
387   yes,no:longint;\r
388   {$endif}\r
389   socktype:integer;\r
390   biniptemp:tbinip;\r
391   origaddr:thostname;\r
392 begin\r
393   if state <> wsclosed then close;\r
394   udp := uppercase(proto) = 'UDP';\r
395   if udp then begin\r
396     socktype := SOCK_DGRAM;\r
397     dgram := true;\r
398   end else socktype := SOCK_STREAM;\r
399   origaddr := addr;\r
400 \r
401   if addr = '' then begin\r
402     {$ifdef ipv6}\r
403     //writeln('ipv6 is defined');\r
404     if not v4listendefault then begin\r
405       //writeln('setting addr to ::');\r
406       addr := '::';\r
407     end else\r
408     {$endif}\r
409     addr := '0.0.0.0';\r
410   end;\r
411   if isbiniplist(addr) then biniptemp := biniplist_get(addr,0) else biniptemp := forwardlookup(addr,10);\r
412   addr := ipbintostr(biniptemp);\r
413   //writeln('after ipbintostr call addr =',addr);\r
414   //writeln('biniptemp.family =',biniptemp.family);\r
415   //writeln('AF_INET6=',AF_INET6);\r
416   //writeln('PF_INET6=',PF_INET6);\r
417   //writeln('AF_INET=',AF_INET);\r
418   //writeln('PF_INET=',PF_INET);\r
419   \r
420   fdhandlein := socket(biniptemp.family,socktype,0);\r
421   {$ifdef ipv6}\r
422   if (addr = '::') and (origaddr = '') and (fdhandlein < 0) then begin\r
423     {writeln('failed to create an IPV6 socket with error ',socketerror,'. trying to create an IPV4 one instead');}\r
424     addr := '0.0.0.0';\r
425     biniptemp := ipstrtobinf(addr);\r
426     fdhandlein := socket(PF_INET,socktype,0);\r
427   end;\r
428   {$endif}\r
429 \r
430   if fdhandlein = -1 then raise ESocketException.create('unable to create socket'{$ifdef mswindows}+' error='+inttostr(wsagetlasterror){$endif});\r
431   dupnowatch(fdhandlein); // sets up maxs and copies handle to fdhandleout among other things\r
432   //eventcore.setfdreverse(fdhandlein,self); //already taken care of by dup\r
433   state := wsclosed; // then set this back as it was an undesired side effect of dup\r
434 \r
435   try\r
436     {$ifndef mswindows}\r
437       yes := $01010101;  {Copied this from existing code. Value is empiric,\r
438                     but works. (yes=true<>0) }\r
439       no := 0;\r
440 \r
441       if SetSocketOptions(fdhandlein, SOL_SOCKET, SO_REUSEADDR,yes,sizeof(yes))=-1 then begin\r
442         raise ESocketException.create('unable to set SO_REUSEADDR socket option');\r
443       end;\r
444       //writeln('addr=',addr);\r
445       //writeln('setting IPV6_V6ONLY option to 0');\r
446       //allow v4 connections on v6 sockets\r
447       if biniptemp.family = af_inet6 then begin\r
448         if SetSocketOptions(fdhandlein, IPPROTO_IPV6,IPV6_V6ONLY,no,sizeof(no))=-1 then begin\r
449           writeln(IPPROTO_IPV6);\r
450           writeln(IPV6_V6ONLY);\r
451           raise ESocketException.create('unable to set IPV6_V6ONLY socket option error='+inttostr(socketerror));\r
452           \r
453         end;\r
454       end;\r
455     {$else}\r
456       SetSockOpt(fdhandlein, SOL_SOCKET, SO_REUSEADDR, 'TRUE', Length('TRUE'));\r
457 \r
458     {$endif}\r
459     localaddr := addr;\r
460     localport := port;\r
461     bindsocket;\r
462 \r
463     if not udp then begin\r
464       {!!! allow custom queue length? default 5}\r
465       if listenqueue = 0 then listenqueue := 5;\r
466       If {$ifdef mswindows}winsock.listen{$else}system_listen{$endif}(fdhandlein,listenqueue)<>{$ifdef mswindows}0{$else}true{$endif} Then raise\r
467 esocketexception.create('unable to listen');\r
468       state := wsListening;\r
469     end else begin\r
470       {$ifndef mswindows}\r
471         SetSocketOptions(fdhandleout, SOL_SOCKET, SO_BROADCAST, 'TRUE', Length('TRUE'));\r
472       {$else}\r
473         SetSockOpt(fdhandleout, SOL_SOCKET, SO_BROADCAST, 'TRUE', Length('TRUE'));\r
474       {$endif}\r
475       state := wsconnected;\r
476     end;\r
477 \r
478     {$ifdef secondlistener}\r
479     //listening on ::. try to listen on 0.0.0.0 as well for platforms which don't already do that\r
480     if addr = '::' then begin\r
481       secondlistener := tlsocket.create(nil);\r
482       secondlistener.proto := proto;\r
483       secondlistener.addr := '0.0.0.0';\r
484       secondlistener.port := port;\r
485       if udp then begin\r
486         secondlistener.ondataavailable := secondaccepthandler;\r
487       end else begin\r
488         secondlistener.onsessionAvailable := secondaccepthandler;\r
489       end;\r
490       try\r
491         secondlistener.listen;\r
492       except\r
493         secondlistener.destroy;\r
494         secondlistener := nil;\r
495       end;\r
496     end;\r
497     {$endif}\r
498   finally\r
499     if state = wsclosed then begin\r
500       if fdhandlein >= 0 then begin\r
501         {one *can* get here without fd -beware}\r
502         eventcore.rmasterclr(fdhandlein);\r
503         myfdclose(fdhandlein); // we musnt leak file descriptors\r
504         eventcore.setfdreverse(fdhandlein,nil);\r
505         fdhandlein := -1;\r
506       end;\r
507     end else begin\r
508       eventcore.rmasterset(fdhandlein,not udp);\r
509     end;\r
510     if fdhandleout >= 0 then eventcore.wmasterclr(fdhandleout);\r
511   end;\r
512   //writeln('listened on addr '+addr+':'+port+' '+proto+' using socket number ',fdhandlein);\r
513 end;\r
514 \r
515 {$ifdef secondlistener}\r
516 procedure tlsocket.internalclose(error:word);\r
517 begin\r
518   if assigned(secondlistener) then begin\r
519     secondlistener.destroy;\r
520     secondlistener := nil;\r
521   end;\r
522   inherited internalclose(error);\r
523 end;\r
524 \r
525 procedure tlsocket.secondaccepthandler;\r
526 begin\r
527   lastsessionfromsecond := true;\r
528   if udp then begin\r
529     ondataavailable(self,error);\r
530   end else begin\r
531     if assigned(onsessionavailable) then onsessionavailable(self,error);\r
532   end;\r
533 end;\r
534 {$endif}\r
535 \r
536 function tlsocket.accept : longint;\r
537 var\r
538   FromAddrSize     : LongInt;        // i don't really know what to do with these at this\r
539   FromAddr         : TInetSockAddrV;  // at this point time will tell :)\r
540   a,acceptlasterror:integer;\r
541 begin\r
542   {$ifdef secondlistener}\r
543   if (lastsessionfromsecond) then begin\r
544     lastsessionfromsecond := false;\r
545     result := secondlistener.accept;\r
546     exit;\r
547   end;\r
548   {$endif}\r
549 \r
550   FromAddrSize := Sizeof(FromAddr);\r
551   {$ifdef mswindows}\r
552     result := winsock.accept(fdhandlein,@fromaddr,@fromaddrsize);\r
553   {$else}\r
554     result := system_accept(fdhandlein,fromaddr,fromaddrsize);\r
555   {$endif}\r
556 \r
557   if (result = -1) then acceptlasterror := {$ifdef mswindows}getlasterror{$else}socketerror{$endif} else acceptlasterror := 0;\r
558 \r
559   //now we have accepted one request start monitoring for more again\r
560   eventcore.rmasterset(fdhandlein,true);\r
561 \r
562   if result = -1 then begin\r
563     raise esocketexception.create('error '+inttostr(acceptlasterror)+' while accepting');\r
564   end;\r
565   if result > absolutemaxs then begin\r
566     myfdclose(result);\r
567     a := result;\r
568 {    result := -1;}\r
569     raise esocketexception.create('file descriptor out of range: '+inttostr(a));\r
570   end;\r
571 end;\r
572 \r
573 \r
574 function tlsocket.sendto(dest:TInetSockAddrV;destlen:integer;data:pointer;len:integer):integer;\r
575 var\r
576   {$ifdef ipv6}\r
577     realdest : tinetsockaddrv;\r
578     biniptemp : tbinip;\r
579   {$endif}\r
580   destx : {$ifdef mswindows}winsock.pSockAddr{$else}pInetSockAddrV{$endif};\r
581 \r
582 begin\r
583   {$ifdef secondlistener}\r
584     if assigned(secondlistener) then if (dest.inaddr.family = AF_INET) then begin\r
585       result := secondlistener.sendto(dest,destlen,data,len);\r
586       exit;\r
587     end;\r
588   {$endif}\r
589   {$ifdef ipv6}\r
590     if isv6socket then begin\r
591       biniptemp := inaddrvtobinip(dest);\r
592       converttov6(biniptemp);\r
593       destlen := makeinaddrv(biniptemp,inttostr(ntohs(dest.InAddr.port)),realdest);\r
594       destx := {$ifdef mswindows}winsock.pSockAddr{$else}pInetSockAddrV{$endif}(@realdest)\r
595     end else begin\r
596       destx := {$ifdef mswindows}winsock.pSockAddr{$else}pInetSockAddrV{$endif}(@dest)\r
597     end;\r
598   {$else}\r
599     destx := {$ifdef mswindows}winsock.pSockAddr{$else}pInetSockAddrV{$endif}(@dest);\r
600   {$endif}\r
601 \r
602   result := {$ifdef mswindows}winsock.sendto{$else}system_sendto{$endif}(self.fdhandleout,data^,len,0,destx^,destlen);\r
603 end;\r
604 \r
605 \r
606 function tlsocket.receivefrom(data:pointer;len:integer;var src:TInetSockAddrV;var srclen:integer):integer;\r
607 var\r
608   tempsrc:TInetSockAddrV;\r
609   tempsrclen:integer;\r
610   srcx : {$ifdef mswindows}winsock.TSockAddr{$else}TInetSockAddrV{$endif} absolute tempsrc;\r
611   biniptemp:tbinip;\r
612 begin\r
613   {$ifdef secondlistener}\r
614   if assigned(secondlistener) then if lastsessionfromsecond then begin\r
615     lastsessionfromsecond := false;\r
616     result := secondlistener.receivefrom(data,len,src,srclen);\r
617     exit;\r
618   end;\r
619   {$endif}\r
620   tempsrclen := sizeof(tempsrc);\r
621   result := recvfrom(self.fdhandlein,data^,len,0,srcx,tempsrclen);\r
622 \r
623   {$ifdef ipv6}\r
624   biniptemp := inaddrvtobinip(tempsrc);\r
625   if needconverttov4(biniptemp) then begin\r
626     converttov4(biniptemp);\r
627     tempsrclen := makeinaddrv(biniptemp,inttostr(ntohs(tempsrc.InAddr.port)),tempsrc);\r
628   end;\r
629   {$endif}\r
630 \r
631   move(tempsrc,src,srclen);\r
632   srclen := tempsrclen;\r
633 end;\r
634 \r
635 procedure tlsocket.taskcallconnectionfailedhandler(wparam,lparam : longint);\r
636 begin\r
637   connectionfailedhandler(wparam);\r
638 end;\r
639 \r
640 procedure tlsocket.connectionfailedhandler(error:word);\r
641 begin\r
642    if trymoreips then begin\r
643 //     writeln('failed with error ',error);\r
644      connecttimeout.enabled := false;\r
645      destroying := true;\r
646      state := wsconnected;\r
647      self.internalclose(0);\r
648      destroying := false;\r
649      realconnect;\r
650    end else begin\r
651      state := wsconnected;\r
652      if assigned(onsessionconnected) then onsessionconnected(self,error);\r
653      self.internalclose(0);\r
654      recvq.del(maxlongint);\r
655    end;\r
656 end;\r
657 \r
658 procedure tlsocket.connectsuccesshandler;\r
659 begin\r
660    trymoreips := false;\r
661    connecttimeout.enabled := false;\r
662    if assigned(onsessionconnected) then onsessionconnected(self,0);\r
663 end;\r
664 \r
665 \r
666 procedure tlsocket.handlefdtrigger(readtrigger,writetrigger:boolean);\r
667 var\r
668   tempbuf:array[0..receivebufsize-1] of byte;\r
669 begin\r
670 //  writeln('got a fd trigger, readtrigger=',readtrigger,' writetrigger=',writetrigger,' state=',integer(state));\r
671   if (state =wslistening) and readtrigger then begin\r
672 {    debugout('listening socket triggered on read');}\r
673     eventcore.rmasterclr(fdhandlein);\r
674     if assigned(onsessionAvailable) then onsessionAvailable(self,0);\r
675   end;\r
676   if dgram and readtrigger then begin\r
677     if assigned(ondataAvailable) then ondataAvailable(self,0);\r
678     {!!!test}\r
679     exit;\r
680   end;\r
681   if (state =wsconnecting) and writetrigger then begin\r
682     // code for dealing with the results of a non-blocking connect is\r
683     // rather complex\r
684     // if just write is triggered it means connect succeeded\r
685     // if both read and write are triggered it can mean 2 things\r
686     // 1: connect ok and data available\r
687     // 2: connect fail\r
688     // to find out which you must read from the socket and look for errors\r
689     // there if we read successfully we drop through into the code for firing\r
690     // the read event\r
691     if not readtrigger then begin\r
692       state := wsconnected;\r
693       connectsuccesshandler;\r
694     end else begin\r
695       numread := myfdread(fdhandlein,tempbuf,sizeof(tempbuf));\r
696       if numread <> -1 then begin\r
697         state := wsconnected;\r
698         connectsuccesshandler;\r
699         //connectread := true;\r
700         recvq.add(@tempbuf,numread);\r
701       end else begin\r
702         connectionfailedhandler({$ifdef mswindows}wsagetlasterror{$else}linuxerror{$endif});\r
703         exit;\r
704       end;\r
705       // if things went well here we are now in the state wsconnected with data sitting in our receive buffer\r
706       // so we drop down into the processing for data available\r
707     end;\r
708     if fdhandlein >= 0 then begin\r
709       if state = wsconnected then begin\r
710         eventcore.rmasterset(fdhandlein,false);\r
711       end else begin\r
712         eventcore.rmasterclr(fdhandlein);\r
713       end;\r
714     end;\r
715     if fdhandleout >= 0 then begin\r
716       if sendq.size = 0 then begin\r
717         //don't clear the bit in fdswmaster if data is in the sendq\r
718         eventcore.wmasterclr(fdhandleout);\r
719       end;\r
720     end;\r
721 \r
722   end;\r
723   inherited handlefdtrigger(readtrigger,writetrigger);\r
724 end;\r
725 \r
726 constructor tlsocket.Create(AOwner: TComponent);\r
727 begin\r
728   inherited create(aowner);\r
729   closehandles := true;\r
730   trymoreips := true;\r
731 end;\r
732 \r
733 \r
734 \r
735 function tlsocket.getpeername(var addr:tsockaddrin;addrlen:integer):integer;\r
736 var\r
737   addrx : {$ifdef mswindows}winsock.tsockaddr{$else}tsockaddrin{$endif} absolute addr;\r
738 begin\r
739   result := {$ifdef mswindows}winsock.getpeername{$else}system_getpeername{$endif}(self.fdhandlein,addrx,addrlen);\r
740 end;\r
741 \r
742 procedure tlsocket.getxaddrbin(var binip:tbinip);\r
743 var\r
744   addr:tinetsockaddrv;\r
745   i:integer;\r
746 begin\r
747   i := sizeof(addr);\r
748   fillchar(addr,sizeof(addr),0);\r
749 \r
750   {$ifdef mswindows}\r
751     winsock.getsockname(self.fdhandlein,psockaddr(@addr)^,i);\r
752   {$else}\r
753     getsocketname(self.fdhandlein,addr,i);\r
754   {$endif}\r
755   binip := inaddrvtobinip(addr);\r
756   converttov4(binip);\r
757 end;\r
758 \r
759 procedure tlsocket.getpeeraddrbin(var binip:tbinip);\r
760 var\r
761   addr:tinetsockaddrv;\r
762   i:integer;\r
763 begin\r
764   i := sizeof(addr);\r
765   fillchar(addr,sizeof(addr),0);\r
766   {$ifdef mswindows}\r
767     winsock.getpeername(self.fdhandlein,psockaddr(@addr)^,i);\r
768   {$else}\r
769     system_getpeername(self.fdhandlein,addr,i);\r
770   {$endif}\r
771 \r
772   binip := inaddrvtobinip(addr);\r
773   converttov4(binip);\r
774 end;\r
775 \r
776 function tlsocket.getXaddr:thostname;\r
777 var\r
778   biniptemp:tbinip;\r
779 begin\r
780   getxaddrbin(biniptemp);\r
781   result := ipbintostr(biniptemp);\r
782   if result = '' then result := 'error';\r
783 end;\r
784 \r
785 function tlsocket.getpeeraddr:thostname;\r
786 var\r
787   biniptemp:tbinip;\r
788 begin\r
789   getpeeraddrbin(biniptemp);\r
790   result := ipbintostr(biniptemp);\r
791   if result = '' then result := 'error';\r
792 end;\r
793 \r
794 function tlsocket.getXport:ansistring;\r
795 var\r
796   addr:tinetsockaddrv;\r
797   i:integer;\r
798 begin\r
799   i := sizeof(addr);\r
800   {$ifdef mswindows}\r
801     winsock.getsockname(self.fdhandlein,psockaddrin(@addr)^,i);\r
802 \r
803   {$else}\r
804     getsocketname(self.fdhandlein,addr,i);\r
805 \r
806   {$endif}\r
807   result := inttostr(htons(addr.InAddr.port));\r
808 end;\r
809 \r
810 function tlsocket.getpeerport:ansistring;\r
811 var\r
812   addr:tinetsockaddrv;\r
813   i:integer;\r
814 begin\r
815   i := sizeof(addr);\r
816   {$ifdef mswindows}\r
817     winsock.getpeername(self.fdhandlein,psockaddrin(@addr)^,i);\r
818 \r
819   {$else}\r
820     system_getpeername(self.fdhandlein,addr,i);\r
821 \r
822   {$endif}\r
823   result := inttostr(htons(addr.InAddr.port));\r
824 end;\r
825 \r
826 {$ifdef mswindows}\r
827   procedure tlsocket.myfdclose(fd : integer);\r
828   begin\r
829     closesocket(fd);\r
830   end;\r
831   function tlsocket.myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt;\r
832   begin\r
833     result := winsock.send(fd,(@buf)^,size,0);\r
834   end;\r
835   function tlsocket.myfdread(fd: LongInt;var buf;size: LongInt):LongInt;\r
836   begin\r
837     result := winsock.recv(fd,buf,size,0);\r
838   end;\r
839 {$endif}\r
840 \r
841 end.\r
842 \r