1 |
/* |
2 |
The Malete project - the Z39.2/Z39.50 database framework of OpenIsis. |
3 |
Version 0.9.x (patchlevel see file Version) |
4 |
Copyright (C) 2001-2004 by Erik Grziwotz, erik@openisis.org |
5 |
|
6 |
This library is free software; you can redistribute it and/or |
7 |
modify it under the terms of the GNU Lesser General Public |
8 |
License as published by the Free Software Foundation; either |
9 |
version 2.1 of the License, or (at your option) any later version. |
10 |
|
11 |
This library is distributed in the hope that it will be useful, |
12 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
14 |
See the GNU Lesser General Public License for more details. |
15 |
|
16 |
You should have received a copy of the GNU Lesser General Public |
17 |
License along with this library; if not, write to the Free Software |
18 |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 |
|
20 |
see README for more information |
21 |
EOH */ |
22 |
|
23 |
/* |
24 |
$Id: srv.c,v 1.7 2004/08/16 12:10:07 kripke Exp $ |
25 |
malete server |
26 |
*/ |
27 |
#ifndef WIN32 |
28 |
#include <sys/types.h> |
29 |
#include <sys/time.h> |
30 |
#include <sys/select.h> |
31 |
#include <sys/socket.h> |
32 |
#include <unistd.h> |
33 |
#include <fcntl.h> |
34 |
#include <netinet/in.h> |
35 |
#include <sys/un.h> |
36 |
#include <string.h> /* memcpy et al */ |
37 |
#include <signal.h> |
38 |
#include <errno.h> |
39 |
#include <sys/wait.h> |
40 |
#else |
41 |
#define WIN32_LEAN_AND_MEAN |
42 |
#define NONAMELESSUNION |
43 |
/* #include <winsock2.h> -lws2_32 */ |
44 |
#include <winsock.h> /* -lwsock32 "good" enough ? */ |
45 |
#define close closesocket |
46 |
#endif |
47 |
|
48 |
#include "../tool/tool.h" |
49 |
|
50 |
|
51 |
static void stdsrv ( file f ) |
52 |
{ |
53 |
List in; |
54 |
FIL_DEFBUF(f); |
55 |
lInit(&in, 0); |
56 |
while ( fGetr(lClr(&in), &fb) ) { |
57 |
dispatch(in.fld); |
58 |
SEOR(env.out); |
59 |
} |
60 |
} /* srv */ |
61 |
|
62 |
|
63 |
static char buf[0x2000]; |
64 |
static int fill; |
65 |
static int crlf; |
66 |
static int wrotehead; |
67 |
static int broken; |
68 |
static unsigned sock; /* response socket */ |
69 |
enum { |
70 |
SOL, /* at start of line */ |
71 |
TAG, /* in tag (only digits seen in line) */ |
72 |
VAL /* in val (some non-digit seen) */ |
73 |
}; |
74 |
|
75 |
static int flushsock () |
76 |
{ |
77 |
int got = send(sock, buf, fill, 0); |
78 |
if ( 0 < got ) { |
79 |
if ( !(fill -= got) ) |
80 |
return 0; |
81 |
memmove(buf, buf+got, fill); |
82 |
return 0; |
83 |
} |
84 |
fill = 0; |
85 |
return broken = eRr(ERR_NO, "could not send"); |
86 |
} |
87 |
|
88 |
|
89 |
static void sinksock (Sink *lo, int eor) |
90 |
{ |
91 |
Fld *f = lo->lst.fld, *e = f + RLEN(f); |
92 |
|
93 |
if ( (unsigned)-1 == sock ) |
94 |
goto reset; |
95 |
if ( wrotehead ) |
96 |
f++; |
97 |
for ( ; f < e; f++ ) { |
98 |
int maxfill = sizeof(buf)-13-f->len, l, n; |
99 |
char *p, *v; |
100 |
if ( 0 > maxfill ) |
101 |
maxfill = 0; |
102 |
/* make sure we have enough room */ |
103 |
while ( fill > maxfill ) |
104 |
if ( flushsock() ) |
105 |
goto reset; |
106 |
p = buf+fill; |
107 |
if ( wrotehead ) { |
108 |
p += i2a(p, f->tag); |
109 |
*p++ = TAB; |
110 |
} else { |
111 |
if ( 10>b36val[(unsigned)*f->val] ) { |
112 |
*p++ = 'W'; |
113 |
*p++ = TAB; |
114 |
} |
115 |
wrotehead = 1; |
116 |
} |
117 |
v = f->val; |
118 |
l = f->len; |
119 |
/* write field value */ |
120 |
while ( l >= (n = buf+sizeof(buf) - p) ) { |
121 |
memcpy(p, v, n); |
122 |
fill = sizeof(buf); |
123 |
if ( flushsock() ) |
124 |
goto reset; |
125 |
p = buf+fill; |
126 |
} |
127 |
memcpy(p, v, l); |
128 |
p += l; |
129 |
*p++ = LF; |
130 |
fill = p - buf; |
131 |
} /* for */ |
132 |
if ( eor ) { /* add a \n and flush */ |
133 |
if ( sizeof(buf) == fill && flushsock() ) /* pathological */ |
134 |
goto reset; |
135 |
buf[fill++] = LF; |
136 |
while ( fill && !flushsock() ) |
137 |
; |
138 |
} |
139 |
reset: |
140 |
lReset(&lo->lst); |
141 |
} /* sinksock */ |
142 |
|
143 |
|
144 |
/* |
145 |
serve one request on sock |
146 |
*/ |
147 |
static int srv1 () |
148 |
{ |
149 |
#define CR 13 |
150 |
int state = SOL, neg = 0; |
151 |
char *p, *e, *n; |
152 |
unsigned l; |
153 |
List in; |
154 |
Fld *f = 0; |
155 |
|
156 |
crlf = 0; |
157 |
lInit(&in, 0); |
158 |
|
159 |
wantmore: /* really a for, but we need multi-level continue anyway */ |
160 |
if ( 0 >= (fill = recv(sock, p = buf, sizeof(buf), 0)) ) |
161 |
return eRr(LOG_WARN, "got %d in recv", fill); |
162 |
LOG_DBG(LOG_DEBUG, "got %d bytes", fill); |
163 |
e = p + fill; |
164 |
havemore: |
165 |
switch (state) { |
166 |
case SOL: |
167 |
switch (*p) { |
168 |
case CR: |
169 |
if (e == p+1) goto wantmore; /* ignore */ |
170 |
if (LF != p[1]) break; /* weird crap */ |
171 |
p++; |
172 |
case LF: /* got blank line */ |
173 |
if ( e > ++p ) |
174 |
return eRr(ERR_INVAL, "got %d excess bytes", e-p); |
175 |
goto gotit; /* break 2 */ |
176 |
} |
177 |
LNEWF(&in, 0, 80); |
178 |
if ( !f && (CT_IS(D,*p) || '-' == *p) ) { |
179 |
eRr(LOG_WARN, "no message name"); |
180 |
LNEWF(&in, 0, 80); |
181 |
} |
182 |
f = LLAST(&in); |
183 |
state = TAG; |
184 |
if ( (neg = '-' == *p) && e == ++p ) goto wantmore; |
185 |
case TAG: |
186 |
while ( CT_IS(D,*p) ) { |
187 |
f->tag = f->tag*10 + b36val[(unsigned char)*p]; |
188 |
if ( e == ++p ) goto wantmore; /* continue 2 */ |
189 |
} |
190 |
if ( neg ) |
191 |
f->tag = -f->tag; |
192 |
LOG_DBG(LOG_DEBUG, "got tag %d", f->tag); |
193 |
state = VAL; |
194 |
if ( TAB == *p && e == ++p ) goto wantmore; |
195 |
case VAL: |
196 |
n = memchr(p, LF, e-p); |
197 |
if ( !n ) |
198 |
l = e-p; |
199 |
else if ( (l = n-p) && 13 == n[-1] ) { |
200 |
if ( crlf ) |
201 |
l--; |
202 |
else if ( f == in.fld ) { |
203 |
l--; |
204 |
crlf = 1; |
205 |
LOG_DBG(LOG_DEBUG, "detected crlf"); |
206 |
} |
207 |
} |
208 |
LAPP(&in, p, l); |
209 |
if ( n ) { |
210 |
state = SOL; |
211 |
if ( e > (p = n+1) ) |
212 |
goto havemore; |
213 |
} |
214 |
goto wantmore; |
215 |
} |
216 |
gotit: |
217 |
fill = 0; |
218 |
wrotehead = 0; |
219 |
broken = 0; |
220 |
env.out->off = 0; |
221 |
lClr(&env.out->lst); |
222 |
dispatch(in.fld); |
223 |
SEOR(env.out); |
224 |
return broken; |
225 |
} /* srv1 */ |
226 |
|
227 |
|
228 |
/* ************************************************************ |
229 |
*/ |
230 |
|
231 |
int server () |
232 |
{ |
233 |
Ses s; |
234 |
Fld opt = { 0, 0, 0 }; |
235 |
fd_set lst; /* listening socks */ |
236 |
fd_set all; |
237 |
unsigned fdlen = 0; |
238 |
#ifndef WIN32 |
239 |
unsigned kids = 0; |
240 |
#endif |
241 |
|
242 |
sInit(ses = &s); |
243 |
FD_ZERO(&lst); |
244 |
FD_ZERO(&all); |
245 |
if ( env.opt ) for ( opt.val = 0; vGet(&opt, env.opt->fld, "S"); ) |
246 |
switch (opt.tag) { |
247 |
static const int yes = !0; |
248 |
struct sockaddr_in si; |
249 |
#ifndef WIN32 |
250 |
struct sockaddr_un su; |
251 |
#endif |
252 |
struct sockaddr *sa; |
253 |
int sal; |
254 |
Fld info; |
255 |
case 'S': /* socket */ |
256 |
info = opt; /* for message */ |
257 |
#ifndef WIN32 |
258 |
if ( opt.len && '/'==*opt.val ) { /* unix socket */ |
259 |
if ( opt.len > sizeof(su.sun_path)-1 ) { |
260 |
eRr(ERR_INVAL, "unix socket name '%.*s' too long", opt.len, opt.val); |
261 |
return 1; |
262 |
} |
263 |
memset(&su, 0, sizeof(su)); |
264 |
memcpy(su.sun_path, opt.val, opt.len); |
265 |
su.sun_path[opt.len] = 0; |
266 |
su.sun_family = AF_UNIX; |
267 |
unlink(su.sun_path); |
268 |
sa = (struct sockaddr*)&su; |
269 |
sal = sizeof(su); |
270 |
sock = socket(PF_UNIX, SOCK_STREAM, 0); |
271 |
} else |
272 |
#else |
273 |
{ |
274 |
WSADATA wsadata; |
275 |
memset(&wsadata, 0, sizeof(wsadata)); |
276 |
if ( WSAStartup(2, &wsadata) ) { |
277 |
/* funny enough, winsock 1 says ok :) */ |
278 |
eRr(ERR_INVAL, "could not get winsock"); |
279 |
return 1; |
280 |
} |
281 |
} |
282 |
#endif |
283 |
{ /* TCP socket */ |
284 |
memset(&si, 0, sizeof(si)); |
285 |
si.sin_family = AF_INET; |
286 |
si.sin_addr.s_addr = INADDR_ANY; |
287 |
si.sin_port = htons(2042); |
288 |
if ( opt.len ) { /* standard */ |
289 |
Fld port = opt; |
290 |
char *end = memchr(opt.val, ':', opt.len); |
291 |
if ( end /* host:port */ |
292 |
|| CT_D!=lat1ct[(unsigned char)*opt.val] /* hostname */ |
293 |
|| memchr(opt.val, '.', opt.len) |
294 |
) { |
295 |
if ( !end ) end = opt.val+opt.len; |
296 |
if ( 9 >= (end - opt.val) |
297 |
&& !memcmp("localhost",opt.val,end - opt.val) |
298 |
) |
299 |
si.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
300 |
else { /* parse dotted decimal */ |
301 |
} |
302 |
if ( (port.len = opt.len - (end - opt.val)) ) { |
303 |
port.len--; |
304 |
port.val = end+1; |
305 |
} |
306 |
} |
307 |
if ( port.len ) |
308 |
si.sin_port = htons(V2I(&port)); |
309 |
} else { |
310 |
info.val = "*:2042"; |
311 |
info.len = 6; |
312 |
} |
313 |
sa = (struct sockaddr *)&si; |
314 |
sal = sizeof(si); |
315 |
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP/*6*/); |
316 |
} |
317 |
if ( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) ) { |
318 |
eRr(ERR_NO, "setsockopt"); |
319 |
return 1; |
320 |
} |
321 |
if ( bind(sock, sa, sal) ) { |
322 |
eRr(ERR_NO, "could not bind to '%.*s'", info.len, info.val); |
323 |
return 1; |
324 |
} |
325 |
if ( listen(sock, 64) ) { |
326 |
eRr(ERR_NO, "listen failed"); |
327 |
return 1; |
328 |
} |
329 |
eRr(LOG_INFO, "listening on '%.*s'", info.len, info.val); |
330 |
FD_SET(sock, &lst); |
331 |
FD_SET(sock, &all); |
332 |
if ( fdlen < sock+1 ) |
333 |
fdlen = sock+1; |
334 |
} |
335 |
/* done with options */ |
336 |
if ( !fdlen ) { |
337 |
stdsrv(env.in); |
338 |
return 0; |
339 |
} |
340 |
env.out->snk = sinksock; |
341 |
for (;;) { |
342 |
fd_set sele = all; |
343 |
int got = select(fdlen, &sele, 0, 0, 0); |
344 |
if ( 0 >= got ) { |
345 |
eRr(ERR_NO, "select said %d", got); |
346 |
continue; |
347 |
} |
348 |
for ( sock=0; got--; ) { |
349 |
while (!FD_ISSET(sock, &sele)) |
350 |
sock++; |
351 |
if ( FD_ISSET(sock, &lst) ) { |
352 |
struct sockaddr peer; |
353 |
unsigned /*socklen_t is broken*/ plen = sizeof(peer); |
354 |
unsigned nsock = accept(sock, &peer, &plen); |
355 |
#ifndef WIN32 |
356 |
if ( ENV_EXCL != env.wri ) { /* shared or read only */ |
357 |
if ( fork() ) { |
358 |
close(nsock); |
359 |
kids++; |
360 |
continue; |
361 |
} |
362 |
sock = nsock; |
363 |
while ( !srv1() ) |
364 |
; |
365 |
exit(0); |
366 |
} |
367 |
#endif |
368 |
FD_SET(nsock, &all); |
369 |
if ( fdlen < nsock+1 ) |
370 |
fdlen = nsock+1; |
371 |
continue; |
372 |
} |
373 |
/* else serve a request on this socket */ |
374 |
/* TODO: use the socket's session */ |
375 |
if ( srv1() ) { |
376 |
close(sock); |
377 |
FD_CLR(sock, &all); |
378 |
} |
379 |
} |
380 |
#ifndef WIN32 |
381 |
for ( ;kids && 0 < waitpid(-1, 0, WNOHANG); kids--) |
382 |
; |
383 |
#endif |
384 |
} |
385 |
return 0; |
386 |
} /* server */ |