1 |
dpavlin |
604 |
/* |
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 */ |