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