All the people who don't use TLS, this one's for you.
[prosody.git] / net / server.lua
1 --[[\r
2 \r
3                 server.lua by blastbeat of the luadch project\r
4                 \r
5                 re-used here under the MIT/X Consortium License\r
6 \r
7                 - this script contains the server loop of the program\r
8                 - other scripts can reg a server here\r
9 \r
10 ]]--\r
11 \r
12 ----------------------------------// DECLARATION //--\r
13 \r
14 --// constants //--\r
15 \r
16 local STAT_UNIT = 1 / ( 1024 * 1024 )    -- mb\r
17 \r
18 --// lua functions //--\r
19 \r
20 local function use( what ) return _G[ what ] end\r
21 \r
22 local type = use "type"\r
23 local pairs = use "pairs"\r
24 local ipairs = use "ipairs"\r
25 local tostring = use "tostring"\r
26 local collectgarbage = use "collectgarbage"\r
27 \r
28 --// lua libs //--\r
29 \r
30 local table = use "table"\r
31 local coroutine = use "coroutine"\r
32 \r
33 --// lua lib methods //--\r
34 \r
35 local table_concat = table.concat\r
36 local table_remove = table.remove\r
37 local string_sub = use'string'.sub\r
38 local coroutine_wrap = coroutine.wrap\r
39 local coroutine_yield = coroutine.yield\r
40 local print = print;\r
41 local out_put = function () end --print;\r
42 local out_error = print;\r
43 \r
44 --// extern libs //--\r
45 \r
46 local luasec = select(2, pcall(require, "ssl"))\r
47 local luasocket = require "socket"\r
48 \r
49 --// extern lib methods //--\r
50 \r
51 local ssl_wrap = ( luasec and luasec.wrap )\r
52 local socket_bind = luasocket.bind\r
53 local socket_select = luasocket.select\r
54 local ssl_newcontext = ( luasec and luasec.newcontext )\r
55 \r
56 --// functions //--\r
57 \r
58 local loop\r
59 local stats\r
60 local addtimer\r
61 local closeall\r
62 local addserver\r
63 local firetimer\r
64 local closesocket\r
65 local removesocket\r
66 local wrapserver\r
67 local wraptcpclient\r
68 local wrapsslclient\r
69 \r
70 --// tables //--\r
71 \r
72 local listener\r
73 local readlist\r
74 local writelist\r
75 local socketlist\r
76 local timelistener\r
77 \r
78 --// simple data types //--\r
79 \r
80 local _\r
81 local readlen = 0    -- length of readlist\r
82 local writelen = 0    -- lenght of writelist\r
83 \r
84 local sendstat= 0\r
85 local receivestat = 0\r
86 \r
87 ----------------------------------// DEFINITION //--\r
88 \r
89 listener = { }    -- key = port, value = table\r
90 readlist = { }    -- array with sockets to read from\r
91 writelist = { }    -- arrary with sockets to write to\r
92 socketlist = { }    -- key = socket, value = wrapped socket\r
93 timelistener = { }\r
94 \r
95 stats = function( )\r
96         return receivestat, sendstat\r
97 end\r
98 \r
99 wrapserver = function( listener, socket, ip, serverport, mode, sslctx )    -- this function wraps a server\r
100 \r
101         local dispatch, disconnect = listener.listener, listener.disconnect    -- dangerous\r
102 \r
103         local wrapclient, err\r
104 \r
105         if sslctx then\r
106                 if not ssl_newcontext then\r
107                         return nil, "luasec not found"\r
108                 end\r
109                 if type( sslctx ) ~= "table" then\r
110                         out_error "server.lua: wrong server sslctx"\r
111                         return nil, "wrong server sslctx"\r
112                 end\r
113                 sslctx, err = ssl_newcontext( sslctx )\r
114                 if not sslctx then\r
115                         err = err or "wrong sslctx parameters"\r
116                         out_error( "server.lua: ", err )\r
117                         return nil, err\r
118                 end\r
119                 wrapclient = wrapsslclient\r
120                 wrapclient = wraptlsclient\r
121         else\r
122                 wrapclient = wraptcpclient\r
123         end\r
124 \r
125         local accept = socket.accept\r
126         local close = socket.close\r
127 \r
128         --// public methods of the object //--    \r
129 \r
130         local handler = { }\r
131 \r
132         handler.shutdown = function( ) end\r
133 \r
134         --[[handler.listener = function( data, err )\r
135                 return ondata( handler, data, err )\r
136         end]]\r
137         handler.ssl = function( )\r
138                 return sslctx and true or false\r
139         end\r
140         handler.close = function( closed )\r
141                 _ = not closed and close( socket )\r
142                 writelen = removesocket( writelist, socket, writelen )\r
143                 readlen = removesocket( readlist, socket, readlen )\r
144                 socketlist[ socket ] = nil\r
145                 handler = nil\r
146         end\r
147         handler.ip = function( )\r
148                 return ip\r
149         end\r
150         handler.serverport = function( )\r
151                 return serverport\r
152         end\r
153         handler.socket = function( )\r
154                 return socket\r
155         end\r
156         handler.receivedata = function( )\r
157                 local client, err = accept( socket )    -- try to accept\r
158                 if client then\r
159                         local ip, clientport = client:getpeername( )\r
160                         client:settimeout( 0 )\r
161                         local handler, client, err = wrapclient( listener, client, ip, serverport, clientport, mode, sslctx )    -- wrap new client socket\r
162                         if err then    -- error while wrapping ssl socket\r
163                                 return false\r
164                         end\r
165                         out_put( "server.lua: accepted new client connection from ", ip, ":", clientport )\r
166                         return dispatch( handler )\r
167                 elseif err then    -- maybe timeout or something else\r
168                         out_put( "server.lua: error with new client connection: ", err )\r
169                         return false\r
170                 end\r
171         end\r
172         return handler\r
173 end\r
174 \r
175 wrapsslclient = function( listener, socket, ip, serverport, clientport, mode, sslctx )    -- this function wraps a ssl cleint\r
176 \r
177         local dispatch, disconnect = listener.listener, listener.disconnect\r
178 \r
179         --// transform socket to ssl object //--\r
180 \r
181         local err\r
182         socket, err = ssl_wrap( socket, sslctx )    -- wrap socket\r
183         if err then\r
184                 out_put( "server.lua: ssl error: ", err )\r
185                 return nil, nil, err    -- fatal error\r
186         end\r
187         socket:settimeout( 0 )\r
188 \r
189         --// private closures of the object //--\r
190 \r
191         local writequeue = { }    -- buffer for messages to send\r
192 \r
193         local eol, fatal_send_error   -- end of buffer\r
194 \r
195         local sstat, rstat = 0, 0\r
196 \r
197         --// local import of socket methods //--\r
198 \r
199         local send = socket.send\r
200         local receive = socket.receive\r
201         local close = socket.close\r
202         --local shutdown = socket.shutdown\r
203 \r
204         --// public methods of the object //--\r
205 \r
206         local handler = { }\r
207 \r
208         handler.getstats = function( )\r
209                 return rstat, sstat\r
210         end\r
211 \r
212         handler.listener = function( data, err )\r
213                 return listener( handler, data, err )\r
214         end\r
215         handler.ssl = function( )\r
216                 return true\r
217         end\r
218         handler.send = function( _, data, i, j )\r
219                         return send( socket, data, i, j )\r
220         end\r
221         handler.receive = function( pattern, prefix )\r
222                         return receive( socket, pattern, prefix )\r
223         end\r
224         handler.shutdown = function( pattern )\r
225                 --return shutdown( socket, pattern )\r
226         end\r
227         handler.close = function( closed )\r
228                 if eol and not fatal_send_error then handler._dispatchdata(); end\r
229                 close( socket )\r
230                 writelen = ( eol and removesocket( writelist, socket, writelen ) ) or writelen\r
231                 readlen = removesocket( readlist, socket, readlen )\r
232                 socketlist[ socket ] = nil\r
233                 out_put "server.lua: closed handler and removed socket from list"\r
234         end\r
235         handler.ip = function( )\r
236                 return ip\r
237         end\r
238         handler.serverport = function( )\r
239                 return serverport\r
240         end\r
241         handler.clientport = function( ) \r
242                 return clientport\r
243         end\r
244 \r
245         handler.write = function( data )\r
246                 if not eol then\r
247                         writelen = writelen + 1\r
248                         writelist[ writelen ] = socket\r
249                         eol = 0\r
250                 end\r
251                 eol = eol + 1\r
252                 writequeue[ eol ] = data\r
253         end\r
254         handler.writequeue = function( )\r
255                 return writequeue\r
256         end\r
257         handler.socket = function( )\r
258                 return socket\r
259         end\r
260         handler.mode = function( )\r
261                 return mode\r
262         end\r
263         handler._receivedata = function( )\r
264                 local data, err, part = receive( socket, mode )    -- receive data in "mode"\r
265                 if not err or ( err == "timeout" or err == "wantread" ) then    -- received something\r
266                         local data = data or part or ""\r
267                         local count = #data * STAT_UNIT\r
268                         rstat = rstat + count\r
269                         receivestat = receivestat + count\r
270                         out_put( "server.lua: read data '", data, "', error: ", err )\r
271                         return dispatch( handler, data, err )\r
272                 else    -- connections was closed or fatal error\r
273                         out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )\r
274                         handler.close( )\r
275                         disconnect( handler, err )\r
276                         writequeue = nil\r
277                         handler = nil\r
278                         return false\r
279                 end\r
280         end\r
281         handler._dispatchdata = function( )    -- this function writes data to handlers\r
282                 local buffer = table_concat( writequeue, "", 1, eol )\r
283                 local succ, err, byte = send( socket, buffer )\r
284                 local count = ( succ or 0 ) * STAT_UNIT\r
285                 sstat = sstat + count\r
286                 sendstat = sendstat + count\r
287                 out_put( "server.lua: sended '", buffer, "', bytes: ", succ, ", error: ", err, ", part: ", byte, ", to: ", ip, ":", clientport )\r
288                 if succ then    -- sending succesful\r
289                         --writequeue = { }\r
290                         eol = nil\r
291                         writelen = removesocket( writelist, socket, writelen )    -- delete socket from writelist\r
292                         return true\r
293                 elseif byte and ( err == "timeout" or err == "wantwrite" ) then    -- want write\r
294                         buffer = string_sub( buffer, byte + 1, -1 )    -- new buffer\r
295                         writequeue[ 1 ] = buffer    -- insert new buffer in queue\r
296                         eol = 1\r
297                         return true\r
298                 else    -- connection was closed during sending or fatal error\r
299                         fatal_send_error = true;\r
300                         out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )\r
301                         handler.close( )\r
302                         disconnect( handler, err )\r
303                         writequeue = nil\r
304                         handler = nil\r
305                         return false\r
306                 end\r
307         end\r
308 \r
309         -- // COMPAT // --\r
310 \r
311         handler.getIp = handler.ip\r
312         handler.getPort = handler.clientport\r
313 \r
314         --// handshake //--\r
315 \r
316         local wrote\r
317 \r
318         handler.handshake = coroutine_wrap( function( client )\r
319                         local err\r
320                         for i = 1, 10 do    -- 10 handshake attemps\r
321                                 _, err = client:dohandshake( )\r
322                                 if not err then\r
323                                         out_put( "server.lua: ssl handshake done" )\r
324                                         writelen = ( wrote and removesocket( writelist, socket, writelen ) ) or writelen\r
325                                         handler.receivedata = handler._receivedata    -- when handshake is done, replace the handshake function with regular functions\r
326                                         handler.dispatchdata = handler._dispatchdata\r
327                                         return dispatch( handler )\r
328                                 else\r
329                                         out_put( "server.lua: error during ssl handshake: ", err )\r
330                                         if err == "wantwrite" then\r
331                                                 if wrote == nil then\r
332                                                         writelen = writelen + 1\r
333                                                         writelist[ writelen ] = client\r
334                                                         wrote = true\r
335                                                 end\r
336                                         end\r
337                                         coroutine_yield( handler, nil, err )    -- handshake not finished\r
338                                 end\r
339                         end\r
340                         _ = err ~= "closed" and close( socket )\r
341                         handler.close( )\r
342                         disconnect( handler, err )\r
343                         writequeue = nil\r
344                         handler = nil\r
345                         return false    -- handshake failed\r
346                 end\r
347         )\r
348         handler.receivedata = handler.handshake\r
349         handler.dispatchdata = handler.handshake\r
350 \r
351         handler.handshake( socket )    -- do handshake\r
352 \r
353         socketlist[ socket ] = handler\r
354         readlen = readlen + 1\r
355         readlist[ readlen ] = socket\r
356 \r
357         return handler, socket\r
358 end\r
359 \r
360 wraptlsclient = function( listener, socket, ip, serverport, clientport, mode, sslctx )    -- this function wraps a tls cleint\r
361 \r
362         local dispatch, disconnect = listener.listener, listener.disconnect\r
363 \r
364         --// transform socket to ssl object //--\r
365 \r
366         local err\r
367 \r
368         socket:settimeout( 0 )\r
369         out_put("setting linger on "..tostring(socket))\r
370         socket:setoption("linger", { on = true, timeout = 10 });\r
371         --// private closures of the object //--\r
372 \r
373         local writequeue = { }    -- buffer for messages to send\r
374 \r
375         local eol, fatal_send_error   -- end of buffer\r
376 \r
377         local sstat, rstat = 0, 0\r
378 \r
379         --// local import of socket methods //--\r
380 \r
381         local send = socket.send\r
382         local receive = socket.receive\r
383         local close = socket.close\r
384         --local shutdown = socket.shutdown\r
385 \r
386         --// public methods of the object //--\r
387 \r
388         local handler = { }\r
389 \r
390         handler.getstats = function( )\r
391                 return rstat, sstat\r
392         end\r
393 \r
394         handler.listener = function( data, err )\r
395                 return listener( handler, data, err )\r
396         end\r
397         handler.ssl = function( )\r
398                 return false\r
399         end\r
400         handler.send = function( _, data, i, j )\r
401                         return send( socket, data, i, j )\r
402         end\r
403         handler.receive = function( pattern, prefix )\r
404                         return receive( socket, pattern, prefix )\r
405         end\r
406         handler.shutdown = function( pattern )\r
407                 --return shutdown( socket, pattern )\r
408         end\r
409         handler.close = function( closed )\r
410                 if eol and not fatal_send_error then handler._dispatchdata(); end\r
411                 close( socket )\r
412                 writelen = ( eol and removesocket( writelist, socket, writelen ) ) or writelen\r
413                 readlen = removesocket( readlist, socket, readlen )\r
414                 socketlist[ socket ] = nil\r
415                 out_put "server.lua: closed handler and removed socket from list"\r
416         end\r
417         handler.ip = function( )\r
418                 return ip\r
419         end\r
420         handler.serverport = function( )\r
421                 return serverport\r
422         end\r
423         handler.clientport = function( ) \r
424                 return clientport\r
425         end\r
426 \r
427         handler.write = function( data )\r
428                 if not eol then\r
429                         writelen = writelen + 1\r
430                         writelist[ writelen ] = socket\r
431                         eol = 0\r
432                 end\r
433                 eol = eol + 1\r
434                 writequeue[ eol ] = data\r
435         end\r
436         handler.writequeue = function( )\r
437                 return writequeue\r
438         end\r
439         handler.socket = function( )\r
440                 return socket\r
441         end\r
442         handler.mode = function( )\r
443                 return mode\r
444         end\r
445         handler._receivedata = function( )\r
446                 local data, err, part = receive( socket, mode )    -- receive data in "mode"\r
447                 if not err or ( err == "timeout" or err == "wantread" ) then    -- received something\r
448                         local data = data or part or ""\r
449                         local count = #data * STAT_UNIT\r
450                         rstat = rstat + count\r
451                         receivestat = receivestat + count\r
452                         --out_put( "server.lua: read data '", data, "', error: ", err )\r
453                         return dispatch( handler, data, err )\r
454                 else    -- connections was closed or fatal error\r
455                         out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )\r
456                         handler.close( )\r
457                         disconnect( handler, err )\r
458                         writequeue = nil\r
459                         handler = nil\r
460                         return false\r
461                 end\r
462         end\r
463         handler._dispatchdata = function( )    -- this function writes data to handlers\r
464                 local buffer = table_concat( writequeue, "", 1, eol )\r
465                 local succ, err, byte = send( socket, buffer )\r
466                 local count = ( succ or 0 ) * STAT_UNIT\r
467                 sstat = sstat + count\r
468                 sendstat = sendstat + count\r
469                 out_put( "server.lua: sended '", buffer, "', bytes: ", succ, ", error: ", err, ", part: ", byte, ", to: ", ip, ":", clientport )\r
470                 if succ then    -- sending succesful\r
471                         --writequeue = { }\r
472                         eol = nil\r
473                         writelen = removesocket( writelist, socket, writelen )    -- delete socket from writelist\r
474                         if handler.need_tls then\r
475                                 out_put("server.lua: connection is ready for tls handshake");\r
476                                 handler.starttls(true);\r
477                                 if handler.need_tls then\r
478                                         out_put("server.lua: uh-oh... we still want tls, something must be wrong");\r
479                                 end\r
480                         end\r
481                         return true\r
482                 elseif byte and ( err == "timeout" or err == "wantwrite" ) then    -- want write\r
483                         buffer = string_sub( buffer, byte + 1, -1 )    -- new buffer\r
484                         writequeue[ 1 ] = buffer    -- insert new buffer in queue\r
485                         eol = 1\r
486                         return true\r
487                 else    -- connection was closed during sending or fatal error\r
488                         fatal_send_error = true; -- :(\r
489                         out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )\r
490                         handler.close( )\r
491                         disconnect( handler, err )\r
492                         writequeue = nil\r
493                         handler = nil\r
494                         return false\r
495                 end\r
496         end\r
497 \r
498         handler.receivedata, handler.dispatchdata = handler._receivedata, handler._dispatchdata;\r
499         -- // COMPAT // --\r
500 \r
501         handler.getIp = handler.ip\r
502         handler.getPort = handler.clientport\r
503 \r
504         --// handshake //--\r
505 \r
506         local wrote, read\r
507         \r
508         handler.starttls = function (now)\r
509                 if not now then out_put("server.lua: we need to do tls, but delaying until later"); handler.need_tls = true; return; end\r
510                 out_put( "server.lua: attempting to start tls on "..tostring(socket) )\r
511                 socket, err = ssl_wrap( socket, sslctx )    -- wrap socket\r
512                 out_put("sslwrapped socket is "..tostring(socket));\r
513                 if err then\r
514                         out_put( "server.lua: ssl error: ", err )\r
515                         return nil, nil, err    -- fatal error\r
516                 end\r
517                 socket:settimeout( 1 )\r
518                 send = socket.send\r
519                 receive = socket.receive\r
520                 close = socket.close\r
521                 handler.ssl = function( )\r
522                         return true\r
523                 end\r
524                 handler.send = function( _, data, i, j )\r
525                         return send( socket, data, i, j )\r
526                 end\r
527                 handler.receive = function( pattern, prefix )\r
528                         return receive( socket, pattern, prefix )\r
529                 end\r
530                 \r
531                 handler.starttls = nil;\r
532                 \r
533                         handler.handshake = coroutine_wrap( function( client )\r
534                                         local err\r
535                                         for i = 1, 10 do    -- 10 handshake attemps\r
536                                                 _, err = client:dohandshake( )\r
537                                                 if not err then\r
538                                                         out_put( "server.lua: ssl handshake done" )\r
539                                                         writelen = ( wrote and removesocket( writelist, socket, writelen ) ) or writelen\r
540                                                         handler.receivedata = handler._receivedata    -- when handshake is done, replace the handshake function with regular functions\r
541                                                         handler.dispatchdata = handler._dispatchdata\r
542                                                         handler.need_tls = nil\r
543                                                         socketlist[ client ] = handler\r
544                                                         readlen = readlen + 1\r
545                                                         readlist[ readlen ] = client                                                                                            \r
546                                                         return true;\r
547                                                 else\r
548                                                         out_put( "server.lua: error during ssl handshake: ", err )\r
549                                                         if err == "wantwrite" then\r
550                                                                 if wrote == nil then\r
551                                                                         writelen = writelen + 1\r
552                                                                         writelist[ writelen ] = client\r
553                                                                         wrote = true\r
554                                                                 end\r
555                                                         end\r
556                                                         coroutine_yield( handler, nil, err )    -- handshake not finished\r
557                                                 end\r
558                                         end\r
559                                         _ = err ~= "closed" and close( socket )\r
560                                         handler.close( )\r
561                                         disconnect( handler, err )\r
562                                         writequeue = nil\r
563                                         handler = nil\r
564                                         return false    -- handshake failed\r
565                                 end\r
566                         )\r
567                         handler.receivedata = handler.handshake\r
568                         handler.dispatchdata = handler.handshake\r
569 \r
570                         handler.handshake( socket )    -- do handshake\r
571                 end\r
572         socketlist[ socket ] = handler\r
573         readlen = readlen + 1\r
574         readlist[ readlen ] = socket\r
575 \r
576         return handler, socket\r
577 end\r
578 \r
579 wraptcpclient = function( listener, socket, ip, serverport, clientport, mode )    -- this function wraps a socket\r
580 \r
581         local dispatch, disconnect = listener.listener, listener.disconnect\r
582 \r
583         --// private closures of the object //--\r
584 \r
585         local writequeue = { }    -- list for messages to send\r
586 \r
587         local eol, fatal_send_error\r
588 \r
589         local rstat, sstat = 0, 0\r
590 \r
591         --// local import of socket methods //--\r
592 \r
593         local send = socket.send\r
594         local receive = socket.receive\r
595         local close = socket.close\r
596         local shutdown = socket.shutdown\r
597 \r
598         --// public methods of the object //--\r
599 \r
600         local handler = { }\r
601 \r
602         handler.getstats = function( )\r
603                 return rstat, sstat\r
604         end\r
605 \r
606         handler.listener = function( data, err )\r
607                 return listener( handler, data, err )\r
608         end\r
609         handler.ssl = function( )\r
610                 return false\r
611         end\r
612         handler.send = function( _, data, i, j )\r
613                         return send( socket, data, i, j )\r
614         end\r
615         handler.receive = function( pattern, prefix )\r
616                         return receive( socket, pattern, prefix )\r
617         end\r
618         handler.shutdown = function( pattern )\r
619                 return shutdown( socket, pattern )\r
620         end\r
621         handler.close = function( closed )\r
622                 if eol and not fatal_send_error then handler.dispatchdata(); end\r
623                 _ = not closed and shutdown( socket )\r
624                 _ = not closed and close( socket )\r
625                 writelen = ( eol and removesocket( writelist, socket, writelen ) ) or writelen\r
626                 readlen = removesocket( readlist, socket, readlen )\r
627                 socketlist[ socket ] = nil\r
628                 out_put "server.lua: closed handler and removed socket from list"\r
629         end\r
630         handler.ip = function( )\r
631                 return ip\r
632         end\r
633         handler.serverport = function( )\r
634                 return serverport\r
635         end\r
636         handler.clientport = function( ) \r
637                 return clientport\r
638         end\r
639         handler.write = function( data )\r
640                 if not eol then\r
641                         writelen = writelen + 1\r
642                         writelist[ writelen ] = socket\r
643                         eol = 0\r
644                 end\r
645                 eol = eol + 1\r
646                 writequeue[ eol ] = data\r
647         end\r
648         handler.writequeue = function( )\r
649                 return writequeue\r
650         end\r
651         handler.socket = function( )\r
652                 return socket\r
653         end\r
654         handler.mode = function( )\r
655                 return mode\r
656         end\r
657         \r
658         handler.receivedata = function( )\r
659                 local data, err, part = receive( socket, mode )    -- receive data in "mode"\r
660                 if not err or ( err == "timeout" or err == "wantread" ) then    -- received something\r
661                         local data = data or part or ""\r
662                         local count = #data * STAT_UNIT\r
663                         rstat = rstat + count\r
664                         receivestat = receivestat + count\r
665                         out_put( "server.lua: read data '", data, "', error: ", err )\r
666                         return dispatch( handler, data, err )\r
667                 else    -- connections was closed or fatal error\r
668                         out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )\r
669                         handler.close( )\r
670                         disconnect( handler, err )\r
671                         writequeue = nil\r
672                         handler = nil\r
673                         return false\r
674                 end\r
675         end\r
676         \r
677         handler.dispatchdata = function( )    -- this function writes data to handlers\r
678                 local buffer = table_concat( writequeue, "", 1, eol )\r
679                 local succ, err, byte = send( socket, buffer )\r
680                 local count = ( succ or 0 ) * STAT_UNIT\r
681                 sstat = sstat + count\r
682                 sendstat = sendstat + count\r
683                 out_put( "server.lua: sended '", buffer, "', bytes: ", succ, ", error: ", err, ", part: ", byte, ", to: ", ip, ":", clientport )\r
684                 if succ then    -- sending succesful\r
685                         --writequeue = { }\r
686                         eol = nil\r
687                         writelen = removesocket( writelist, socket, writelen )    -- delete socket from writelist\r
688                         return true\r
689                 elseif byte and ( err == "timeout" or err == "wantwrite" ) then    -- want write\r
690                         buffer = string_sub( buffer, byte + 1, -1 )    -- new buffer\r
691                         writequeue[ 1 ] = buffer    -- insert new buffer in queue\r
692                         eol = 1\r
693                         return true\r
694                 else    -- connection was closed during sending or fatal error\r
695                         fatal_send_error = true; -- :'-(\r
696                         out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )\r
697                         handler.close( )\r
698                         disconnect( handler, err )\r
699                         writequeue = nil\r
700                         handler = nil\r
701                         return false\r
702                 end\r
703         end\r
704 \r
705         -- // COMPAT // --\r
706 \r
707         handler.getIp = handler.ip\r
708         handler.getPort = handler.clientport\r
709 \r
710         socketlist[ socket ] = handler\r
711         readlen = readlen + 1\r
712         readlist[ readlen ] = socket\r
713 \r
714         return handler, socket\r
715 end\r
716 \r
717 addtimer = function( listener )\r
718         timelistener[ #timelistener + 1 ] = listener\r
719 end\r
720 \r
721 firetimer = function( listener )\r
722         for i, listener in ipairs( timelistener ) do\r
723                 listener( )\r
724         end\r
725 end\r
726 \r
727 addserver = function( listeners, port, addr, mode, sslctx )    -- this function provides a way for other scripts to reg a server\r
728         local err\r
729         if type( listeners ) ~= "table" then\r
730                 err = "invalid listener table"\r
731         else\r
732                 for name, func in pairs( listeners ) do\r
733                         if type( func ) ~= "function" then\r
734                                 --err = "invalid listener function"\r
735                                 break\r
736                         end\r
737                 end\r
738         end\r
739         if not type( port ) == "number" or not ( port >= 0 and port <= 65535 ) then\r
740                 err = "invalid port"\r
741         elseif listener[ port ] then\r
742                 err=  "listeners on port '" .. port .. "' already exist"\r
743         elseif sslctx and not luasec then\r
744                 err = "luasec not found"\r
745         end\r
746         if err then\r
747                 out_error( "server.lua: ", err )\r
748                 return nil, err\r
749         end\r
750         addr = addr or "*"\r
751         local server, err = socket_bind( addr, port )\r
752         if err then\r
753                 out_error( "server.lua: ", err )\r
754                 return nil, err\r
755         end\r
756         local handler, err = wrapserver( listeners, server, addr, port, mode, sslctx )    -- wrap new server socket\r
757         if not handler then\r
758                 server:close( )\r
759                 return nil, err\r
760         end\r
761         server:settimeout( 0 )\r
762         readlen = readlen + 1\r
763         readlist[ readlen ] = server\r
764         listener[ port ] = listeners\r
765         socketlist[ server ] = handler\r
766         out_put( "server.lua: new server listener on ", addr, ":", port )\r
767         return true\r
768 end\r
769 \r
770 removesocket = function( tbl, socket, len )    -- this function removes sockets from a list\r
771         for i, target in ipairs( tbl ) do\r
772                 if target == socket then\r
773                         len = len - 1\r
774                         table_remove( tbl, i )\r
775                         return len\r
776                 end\r
777         end\r
778         return len\r
779 end\r
780 \r
781 closeall = function( )\r
782         for sock, handler in pairs( socketlist ) do\r
783                 handler.shutdown( )\r
784                 handler.close( )\r
785                 socketlist[ sock ] = nil\r
786         end\r
787         writelist, readlist, socketlist = { }, { }, { }\r
788 end\r
789 \r
790 closesocket = function( socket )\r
791         writelen = removesocket( writelist, socket, writelen )\r
792         readlen = removesocket( readlist, socket, readlen )\r
793         socketlist[ socket ] = nil\r
794         socket:close( )\r
795 end\r
796 \r
797 loop = function( )    -- this is the main loop of the program\r
798         --signal_set( "hub", "run" )\r
799         repeat\r
800                 --[[print(readlen, writelen)\r
801                 for _, s in ipairs(readlist) do print("R:", tostring(s)) end\r
802                 for _, s in ipairs(writelist) do print("W:", tostring(s)) end\r
803                 out_put("select()"..os.time())]]\r
804                 local read, write, err = socket_select( readlist, writelist, 1 )    -- 1 sec timeout, nice for timers\r
805                 for i, socket in ipairs( write ) do    -- send data waiting in writequeues\r
806                         local handler = socketlist[ socket ]\r
807                         if handler then\r
808                                 handler.dispatchdata( )\r
809                         else\r
810                                 closesocket( socket )\r
811                                 out_put "server.lua: found no handler and closed socket (writelist)"    -- this should not happen\r
812                         end\r
813                 end\r
814                 for i, socket in ipairs( read ) do    -- receive data\r
815                         local handler = socketlist[ socket ]\r
816                         if handler then\r
817                                 handler.receivedata( )\r
818                         else\r
819                                 closesocket( socket )\r
820                                 out_put "server.lua: found no handler and closed socket (readlist)"    -- this can happen\r
821                         end\r
822                 end\r
823                 firetimer( )\r
824         until false\r
825         return\r
826 end\r
827 \r
828 ----------------------------------// BEGIN //--\r
829 \r
830 ----------------------------------// PUBLIC INTERFACE //--\r
831 \r
832 return {\r
833 \r
834         add = addserver,\r
835         loop = loop,\r
836         stats = stats,\r
837         closeall = closeall,\r
838         addtimer = addtimer,\r
839         wraptlsclient = wraptlsclient,\r
840 }\r