Commit | Line | Data |
---|---|---|
de5b4dd4 C |
1 | /*********************************************************** |
2 | Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts, | |
3 | and the Massachusetts Institute of Technology, Cambridge, Massachusetts. | |
4 | ||
5 | All Rights Reserved | |
6 | ||
7 | Permission to use, copy, modify, and distribute this software and its | |
8 | documentation for any purpose and without fee is hereby granted, | |
9 | provided that the above copyright notice appear in all copies and that | |
10 | both that copyright notice and this permission notice appear in | |
11 | supporting documentation, and that the names of Digital or MIT not be | |
12 | used in advertising or publicity pertaining to distribution of the | |
13 | software without specific, written prior permission. | |
14 | ||
15 | DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING | |
16 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL | |
17 | DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR | |
18 | ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
19 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | |
20 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | |
21 | SOFTWARE. | |
22 | ||
23 | ******************************************************************/ | |
24 | /* $XConsortium: connection.c,v 1.88 88/10/22 22:06:31 keith Exp $ */ | |
25 | /***************************************************************** | |
26 | * Stuff to create connections --- OS dependent | |
27 | * | |
28 | * EstablishNewConnections, CreateWellKnownSockets, ResetWellKnownSockets, | |
29 | * CloseDownConnection, CheckConnections, AddEnabledDevice, | |
30 | * RemoveEnabledDevice, OnlyListToOneClient, | |
31 | * ListenToAllClients, | |
32 | * | |
33 | * (WaitForSomething is in its own file) | |
34 | * | |
35 | * In this implementation, a client socket table is not kept. | |
36 | * Instead, what would be the index into the table is just the | |
37 | * file descriptor of the socket. This won't work for if the | |
38 | * socket ids aren't small nums (0 - 2^8) | |
39 | * | |
40 | *****************************************************************/ | |
41 | ||
42 | #ifdef ISOCONN | |
43 | #include <math.h> | |
44 | #endif /* ISOCONN */ | |
45 | ||
46 | #include <dbm.h> | |
47 | #undef NULL | |
48 | #include "X.h" | |
49 | #include "Xproto.h" | |
50 | #include <sys/param.h> | |
51 | #include <errno.h> | |
52 | #include "Xos.h" /* for strings, file, time */ | |
53 | #include <sys/socket.h> | |
54 | ||
55 | #include <signal.h> | |
56 | #include <fcntl.h> | |
57 | #include <setjmp.h> | |
58 | ||
59 | #ifdef hpux | |
60 | #include <sys/ioctl.h> | |
61 | #endif | |
62 | ||
63 | #ifdef TCPCONN | |
64 | #include <netinet/in.h> | |
65 | #ifndef hpux | |
66 | #include <netinet/tcp.h> | |
67 | #endif | |
68 | #endif | |
69 | ||
70 | #ifdef UNIXCONN | |
71 | /* | |
72 | * sites should be careful to have separate /tmp directories for diskless nodes | |
73 | */ | |
74 | #include <sys/un.h> | |
75 | #include <sys/stat.h> | |
76 | static int unixDomainConnection = -1; | |
77 | #endif | |
78 | ||
79 | #include <stdio.h> | |
80 | #include <sys/uio.h> | |
81 | #include "osstruct.h" | |
82 | #include "osdep.h" | |
83 | #include "opaque.h" | |
84 | #include "dixstruct.h" | |
85 | ||
86 | #ifdef DNETCONN | |
87 | #include <netdnet/dn.h> | |
88 | #endif /* DNETCONN */ | |
89 | ||
90 | #ifdef ISOCONN | |
91 | #include <isode/psap.h> | |
92 | #include <isode/tsap.h> | |
93 | #include <isode/isoservent.h> | |
94 | ||
95 | extern char *isodetcpath; | |
96 | ||
97 | #ifdef ISODEBUG | |
98 | /* | |
99 | * Set to true for loads of TSAP messages... | |
100 | */ | |
101 | int isodexbug = FALSE; | |
102 | #endif /* ISODEBUG */ | |
103 | ||
104 | /* | |
105 | * array of fd 2 family map so we can lookup right function below... | |
106 | * Its initialised at connection setup... | |
107 | */ | |
108 | int fd2family[MAXSOCKS]; | |
109 | /* | |
110 | * Globals for storing functions appropos each fd/socket type | |
111 | * UNIX_IO (0) map to sys calls | |
112 | * ISODE_IO (1) maps to my fns... | |
113 | */ | |
114 | ||
115 | extern int accept(), TAcceptFromClient(); | |
116 | int (*acceptfn[])() = | |
117 | { | |
118 | accept, TAcceptFromClient | |
119 | }; | |
120 | extern getpeername(), getISOpeername(); | |
121 | int (*getpeerfn[])() = | |
122 | { | |
123 | getpeername, getISOpeername | |
124 | }; | |
125 | /* | |
126 | * Note yuckiness here XXX | |
127 | * TReadFromClient takes one more param than read - must fix... | |
128 | */ | |
129 | extern int read(), TReadFromClient(); | |
130 | int (*readfn[])() = | |
131 | { | |
132 | read, TReadFromClient | |
133 | }; | |
134 | extern int write(), TWriteToClient(); | |
135 | int (*writefn[])() = | |
136 | { | |
137 | write, TWriteToClient | |
138 | }; | |
139 | extern writev(), TWritevToClient(); | |
140 | int (*writevfn[])() = | |
141 | { | |
142 | writev, TWritevToClient | |
143 | }; | |
144 | extern int close(), TDiscFromClient(); | |
145 | int (*closefn[])() = | |
146 | { | |
147 | close, TDiscFromClient | |
148 | }; | |
149 | ||
150 | #endif /* ISOCONN */ | |
151 | ||
152 | typedef long CCID; /* mask of indices into client socket table */ | |
153 | ||
154 | #ifndef X_UNIX_PATH | |
155 | #define X_UNIX_DIR "/tmp/.X11-unix" | |
156 | #define X_UNIX_PATH "/tmp/.X11-unix/X" | |
157 | #endif | |
158 | ||
159 | #ifdef ISOCONN | |
160 | char *display = NULLCP; /* The display number */ | |
161 | #else /* ISOCONN */ | |
162 | char *display; /* The display number */ | |
163 | #endif /* ISOCONN */ | |
164 | int lastfdesc; /* maximum file descriptor */ | |
165 | ||
166 | long WellKnownConnections; /* Listener mask */ | |
167 | long EnabledDevices; /* mask for input devices that are on */ | |
168 | long AllSockets[mskcnt]; /* select on this */ | |
169 | long AllClients[mskcnt]; /* available clients */ | |
170 | long LastSelectMask[mskcnt]; /* mask returned from last select call */ | |
171 | long ClientsWithInput[mskcnt]; /* clients with FULL requests in buffer */ | |
172 | long ClientsWriteBlocked[mskcnt];/* clients who cannot receive output */ | |
173 | long OutputPending[mskcnt]; /* clients with reply/event data ready to go */ | |
174 | long MaxClients = MAXSOCKS ; | |
175 | long OutputBufferSize = BUFSIZ; /* output buffer size (must be > 0) */ | |
176 | long NConnBitArrays = mskcnt; | |
177 | long FirstClient; | |
178 | Bool NewOutputPending; /* not yet attempted to write some new output */ | |
179 | Bool AnyClientsWriteBlocked; /* true if some client blocked on write */ | |
180 | ||
181 | static Bool debug_conns = FALSE; | |
182 | ||
183 | static char whichByteIsFirst; | |
184 | ||
185 | static int SavedAllClients[mskcnt]; | |
186 | static int SavedAllSockets[mskcnt]; | |
187 | static int SavedClientsWithInput[mskcnt]; | |
188 | static Bool GrabDone = FALSE; | |
189 | ||
190 | ClientPtr ConnectionTranslation[MAXSOCKS]; | |
191 | extern ClientPtr NextAvailableClient(); | |
192 | ||
193 | extern ConnectionInput inputBuffers[]; | |
194 | ||
195 | int swappedClients[MAXSOCKS]; | |
196 | ||
197 | extern int AutoResetServer(); | |
198 | extern int GiveUp(); | |
199 | ||
200 | #ifdef UNIXCONN | |
201 | ||
202 | static struct sockaddr_un unsock; | |
203 | ||
204 | static int open_unix_socket () | |
205 | { | |
206 | int oldUmask; | |
207 | int request; | |
208 | ||
209 | unsock.sun_family = AF_UNIX; | |
210 | oldUmask = umask (0); | |
211 | #ifdef X_UNIX_DIR | |
212 | mkdir (X_UNIX_DIR, 0777); | |
213 | #endif | |
214 | strcpy (unsock.sun_path, X_UNIX_PATH); | |
215 | strcat (unsock.sun_path, display); | |
216 | unlink (unsock.sun_path); | |
217 | if ((request = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) | |
218 | { | |
219 | Notice ("Creating Unix socket"); | |
220 | } | |
221 | else | |
222 | { | |
223 | if(bind(request,(struct sockaddr *)&unsock, strlen(unsock.sun_path)+2)) | |
224 | Error ("Binding Unix socket"); | |
225 | if (listen (request, 5)) Error ("Unix Listening"); | |
226 | } | |
227 | (void)umask(oldUmask); | |
228 | return request; | |
229 | } | |
230 | #endif /*UNIXCONN */ | |
231 | ||
232 | /***************** | |
233 | * CreateWellKnownSockets | |
234 | * At initialization, create the sockets to listen on for new clients. | |
235 | * There are potentially 4: DECnet, UNIX Domain, TCP-IP with MSB first, | |
236 | * with TCP-IP with LSB first. | |
237 | *****************/ | |
238 | ||
239 | void | |
240 | CreateWellKnownSockets() | |
241 | { | |
242 | int request, i; | |
243 | int whichbyte; /* used to figure out whether this is | |
244 | LSB or MSB */ | |
245 | #ifdef TCPCONN | |
246 | struct sockaddr_in insock; | |
247 | int tcpportReg; /* port with same byte order as server */ | |
248 | ||
249 | #ifdef SO_LINGER | |
250 | static int linger[2] = { 0, 0 }; | |
251 | #endif /* SO_LINGER */ | |
252 | ||
253 | #endif /* TCPCONN */ | |
254 | ||
255 | #ifdef DNETCONN | |
256 | struct sockaddr_dn dnsock; | |
257 | #endif /* DNETCONN */ | |
258 | ||
259 | #ifdef ISOCONN | |
260 | struct TSAPdisconnect tds; | |
261 | struct TSAPdisconnect *td = &tds; | |
262 | struct TSAPaddr tas; | |
263 | struct TSAPaddr *ta = &tas; | |
264 | struct PSAPaddr *pa; | |
265 | AEI aei; | |
266 | #endif /* ISOCONN */ | |
267 | int retry; | |
268 | ||
269 | #ifdef ISOCONN | |
270 | #ifdef ISODEBUG | |
271 | isodetcpath = ISODEPATH; | |
272 | #endif | |
273 | #endif /* ISOCONN */ | |
274 | ||
275 | CLEARBITS(AllSockets); | |
276 | CLEARBITS(AllClients); | |
277 | CLEARBITS(LastSelectMask); | |
278 | CLEARBITS(ClientsWithInput); | |
279 | ||
280 | for (i=0; i<MAXSOCKS; i++) ConnectionTranslation[i] = (ClientPtr)NULL; | |
281 | ||
282 | #ifdef hpux | |
283 | lastfdesc = _NFILE - 1; | |
284 | #else | |
285 | lastfdesc = getdtablesize() - 1; | |
286 | #endif /* hpux */ | |
287 | ||
288 | if (lastfdesc > MAXSOCKS) | |
289 | { | |
290 | lastfdesc = MAXSOCKS; | |
291 | if (debug_conns) | |
292 | ErrorF( "GOT TO END OF SOCKETS %d\n", MAXSOCKS); | |
293 | } | |
294 | ||
295 | WellKnownConnections = 0; | |
296 | whichbyte = 1; | |
297 | ||
298 | if (*(char *) &whichbyte) | |
299 | whichByteIsFirst = 'l'; | |
300 | else | |
301 | whichByteIsFirst = 'B'; | |
302 | ||
303 | ||
304 | #ifdef TCPCONN | |
305 | ||
306 | tcpportReg = atoi (display); | |
307 | tcpportReg += X_TCP_PORT; | |
308 | ||
309 | if ((request = socket (AF_INET, SOCK_STREAM, 0)) < 0) | |
310 | { | |
311 | Notice ("Creating TCP socket"); | |
312 | } | |
313 | else | |
314 | { | |
315 | bzero ((char *)&insock, sizeof (insock)); | |
316 | insock.sin_family = AF_INET; | |
317 | insock.sin_port = htons (tcpportReg); | |
318 | insock.sin_addr.s_addr = htonl(INADDR_ANY); | |
319 | retry = 20; | |
320 | while (i = bind(request, (struct sockaddr *) &insock, sizeof (insock))) | |
321 | { | |
322 | #ifdef hpux | |
323 | /* Necesary to restart the server without a reboot */ | |
324 | if (errno == EADDRINUSE) | |
325 | set_socket_option (request, SO_REUSEADDR); | |
326 | if (--retry == 0) | |
327 | Error ("Binding TCP socket"); | |
328 | sleep (1); | |
329 | #else | |
330 | if (--retry == 0) | |
331 | Error ("Binding MSB TCP socket"); | |
332 | sleep (10); | |
333 | #endif /* hpux */ | |
334 | } | |
335 | #ifdef hpux | |
336 | /* return the socket option to the original */ | |
337 | if (errno) | |
338 | unset_socket_option (request, SO_REUSEADDR); | |
339 | #endif /* hpux */ | |
340 | #ifdef SO_LINGER | |
341 | if(setsockopt (request, SOL_SOCKET, SO_LINGER, | |
342 | (char *)linger, sizeof(linger))) | |
343 | Notice ("Setting TCP SO_LINGER\n"); | |
344 | #endif /* SO_LINGER */ | |
345 | if (listen (request, 5)) | |
346 | Error ("Reg TCP Listening"); | |
347 | WellKnownConnections |= (1 << request); | |
348 | DefineSelf (request); | |
349 | #ifdef ISOCONN | |
350 | fd2family[request] = UNIX_IO; | |
351 | #endif /* ISOCONN */ | |
352 | } | |
353 | ||
354 | #endif /* TCPCONN */ | |
355 | ||
356 | #ifdef UNIXCONN | |
357 | if ((request = open_unix_socket ()) != -1) { | |
358 | WellKnownConnections |= (1L << request); | |
359 | unixDomainConnection = request; | |
360 | #ifdef ISOCONN | |
361 | fd2family[request] = UNIX_IO; | |
362 | #endif /* ISOCONN */ | |
363 | } | |
364 | #endif /*UNIXCONN */ | |
365 | ||
366 | #ifdef DNETCONN | |
367 | if ((request = socket (AF_DECnet, SOCK_STREAM, 0)) < 0) | |
368 | { | |
369 | Notice ("Creating DECnet socket"); | |
370 | } | |
371 | else | |
372 | { | |
373 | bzero ((char *)&dnsock, sizeof (dnsock)); | |
374 | dnsock.sdn_family = AF_DECnet; | |
375 | sprintf(dnsock.sdn_objname, "X$X%d", atoi (display)); | |
376 | dnsock.sdn_objnamel = strlen(dnsock.sdn_objname); | |
377 | if (bind (request, (struct sockaddr *) &dnsock, sizeof (dnsock))) | |
378 | Error ("Binding DECnet socket"); | |
379 | if (listen (request, 5)) Error ("DECnet Listening"); | |
380 | WellKnownConnections |= (1 << request); | |
381 | DefineSelf (request); | |
382 | #ifdef ISOCONN | |
383 | fd2family[request] = UNIX_IO; | |
384 | #endif /* ISOCONN */ | |
385 | } | |
386 | #endif /* DNETCONN */ | |
387 | #ifdef ISOCONN | |
388 | /* | |
389 | * If display is set, its the string after the Colon: | |
390 | * i.e. X0 or X1 or T0 or T1... | |
391 | */ | |
392 | if ((display == NULLCP) || (atoi(display) == 0)) | |
393 | aei = str2aei(TLocalHostName(), DEFAULTTSERVICE); | |
394 | else | |
395 | aei = str2aei(TLocalHostName(), display); | |
396 | ||
397 | if (aei == NULLAEI) { | |
398 | ErrorF("No AEI for me:"); | |
399 | FatalError(TLocalHostName()); | |
400 | } | |
401 | ||
402 | /* | |
403 | * This hack only works if the PSAPaddr and SSAP addrsd are null!! | |
404 | */ | |
405 | if ((pa = aei2addr (aei)) == NULLPA) | |
406 | FatalError("address translation failed"); | |
407 | ||
408 | ta = (struct TSAPaddr *)&(pa->pa_addr.sa_addr); | |
409 | ||
410 | /* | |
411 | * Just put out a listen for now | |
412 | */ | |
413 | if ((request = TNetListen(ta, td)) != OK) { | |
414 | Error(TErrString(td->td_reason)); | |
415 | FatalError("TNetListen"); | |
416 | } | |
417 | ||
418 | WellKnownConnections |= (1 << request); | |
419 | DefineSelf (request); | |
420 | fd2family[request] = ISODE_IO; | |
421 | #endif /* ISOCONN */ | |
422 | if (WellKnownConnections == 0) | |
423 | Error ("No Listeners, nothing to do"); | |
424 | signal (SIGPIPE, SIG_IGN); | |
425 | signal (SIGHUP, AutoResetServer); | |
426 | signal (SIGINT, GiveUp); | |
427 | signal (SIGTERM, GiveUp); | |
428 | FirstClient = request + 1; | |
429 | AllSockets[0] = WellKnownConnections; | |
430 | ResetHosts(display); | |
431 | ||
432 | for (i=0; i<MaxClients; i++) | |
433 | { | |
434 | inputBuffers[i].buffer = (char *) NULL; | |
435 | inputBuffers[i].bufptr = (char *) NULL; | |
436 | inputBuffers[i].bufcnt = 0; | |
437 | inputBuffers[i].lenLastReq = 0; | |
438 | inputBuffers[i].size = 0; | |
439 | } | |
440 | } | |
441 | ||
442 | void | |
443 | ResetWellKnownSockets () | |
444 | { | |
445 | #ifdef UNIXCONN | |
446 | if (unixDomainConnection != -1) | |
447 | { | |
448 | /* | |
449 | * see if the unix domain socket has disappeared | |
450 | */ | |
451 | struct stat statb; | |
452 | ||
453 | if (stat (unsock.sun_path, &statb) == -1 || | |
454 | (statb.st_mode & S_IFMT) != S_IFSOCK) | |
455 | { | |
456 | ErrorF ("Unix domain socket %s trashed, recreating\n", | |
457 | unsock.sun_path); | |
458 | (void) unlink (unsock.sun_path); | |
459 | (void) close (unixDomainConnection); | |
460 | WellKnownConnections &= ~(1L << unixDomainConnection); | |
461 | unixDomainConnection = open_unix_socket (); | |
462 | if (unixDomainConnection != -1) | |
463 | WellKnownConnections |= (1L << unixDomainConnection); | |
464 | } | |
465 | } | |
466 | #endif /* UNIXCONN */ | |
467 | } | |
468 | ||
469 | #ifdef ISOCONN | |
470 | /* | |
471 | * Convenience routine... | |
472 | * client = transport descriptor | |
473 | * data, size = buffer | |
474 | * nonblock = NOTOK, blocks, OK, non blocks | |
475 | */ | |
476 | TReadFromClient(client, data, size, nonblock) | |
477 | int client, size, nonblock; | |
478 | char *data; | |
479 | { | |
480 | struct TSAPdisconnect tds; | |
481 | struct TSAPdisconnect *td = &tds; | |
482 | int ret; | |
483 | static struct TSAPdata txs; | |
484 | static struct TSAPdata *tx = &txs; | |
485 | char *aptr; | |
486 | static struct qbuf *qb; | |
487 | static char *qptr; | |
488 | static int ingot, qcpy, result = 0; | |
489 | int q2data; | |
490 | ||
491 | #ifdef ISODEBUG | |
492 | if (isodexbug) { | |
493 | fprintf(stderr, "TReadFromClient %d want %d (%d buffered)\n", | |
494 | client, size, result); | |
495 | } | |
496 | #endif /* ISODEBUG */ | |
497 | if (result == 0) { | |
498 | if ((ret = TReadRequest(client, tx, nonblock, td)) == NOTOK) { | |
499 | #ifdef ISODEBUG | |
500 | if(errno == EWOULDBLOCK ) { | |
501 | fprintf(stderr, "Server TReadReq: would block %s\n", | |
502 | TErrString(td->td_reason)); | |
503 | if (!DR_FATAL(td->td_reason)) | |
504 | errno = EWOULDBLOCK; | |
505 | else | |
506 | errno = EBADF; | |
507 | return ret; | |
508 | } | |
509 | if (isodexbug) | |
510 | fprintf(stderr, "Server TReadReq: %s\n", | |
511 | TErrString(td->td_reason)); | |
512 | #endif /* ISODEBUG */ | |
513 | /* | |
514 | * map problems here - eg TimeOut... | |
515 | */ | |
516 | if (td->td_reason == DR_TIMER) | |
517 | errno = EWOULDBLOCK; | |
518 | return ret; | |
519 | } | |
520 | result = tx->tx_cc; | |
521 | qb = &(tx->tx_qbuf); | |
522 | qptr = qb->qb_data; | |
523 | #ifdef ISODEBUG | |
524 | if (isodexbug) | |
525 | fprintf(stderr, "TReadRequest want %d got %d\n", | |
526 | size, result); | |
527 | #endif | |
528 | } | |
529 | #ifdef ISODEBUG | |
530 | else { | |
531 | if (isodexbug) | |
532 | fprintf(stderr, "TReadFromClient want %d buffered %d\n", | |
533 | size, result); | |
534 | } | |
535 | #endif | |
536 | /* | |
537 | * Buffer it | |
538 | */ | |
539 | ingot = 0; | |
540 | aptr = data; | |
541 | for(ingot = 0, aptr = data, q2data = min(size, result); | |
542 | ingot<q2data; | |
543 | aptr += qcpy, ingot+= qcpy) { | |
544 | int aleft = q2data - ingot; | |
545 | if (qb->qb_len > aleft) { | |
546 | qcpy = aleft; | |
547 | bcopy(qptr, aptr, qcpy); | |
548 | qptr += aleft; | |
549 | } else { | |
550 | qcpy = qb->qb_len; | |
551 | bcopy(qb->qb_data, aptr, qcpy); | |
552 | if ((qb = qb->qb_forw) == NULL) | |
553 | break; | |
554 | qptr = qb->qb_data; | |
555 | } | |
556 | } | |
557 | if ((result -= ingot) <= 0) { | |
558 | result = 0; | |
559 | TXFREE(tx); | |
560 | } | |
561 | return ingot; | |
562 | } | |
563 | ||
564 | #endif /* ISOCONN */ | |
565 | ||
566 | /* We want to read the connection information. If the client doesn't | |
567 | * send us enough data, however, we want to time out eventually. | |
568 | * The scheme is to clear a flag, set an alarm, and keep doing non-blocking | |
569 | * reads until we get all the data we want. If the alarm goes | |
570 | * off, the handler will clear the flag. If we see that the flag is | |
571 | * cleared, we know we've timed out and return with an error. | |
572 | * | |
573 | * there remains one problem with this code: | |
574 | * there is a window of vulnerability in which we might get an alarm | |
575 | * even though all the data has come in properly. This is because I | |
576 | * can't atomically clear the alarm. | |
577 | * | |
578 | * Anyone who sees how to fix this problem should do so and | |
579 | * submit a fix. | |
580 | */ | |
581 | ||
582 | jmp_buf env; | |
583 | void TimeOut() | |
584 | { | |
585 | longjmp(env, 1); | |
586 | } | |
587 | static Bool | |
588 | ReadBuffer(conn, buffer, charsWanted) | |
589 | long conn; | |
590 | char *buffer; | |
591 | int charsWanted; | |
592 | { | |
593 | char *bptr = buffer; | |
594 | int got, fTimeOut; | |
595 | struct itimerval itv; | |
596 | ||
597 | signal(SIGALRM, TimeOut); | |
598 | fTimeOut = FALSE; | |
599 | /* only 1 alarm, please, not 1 per minute */ | |
600 | timerclear(&itv.it_interval); | |
601 | itv.it_value.tv_sec = TimeOutValue; | |
602 | itv.it_value.tv_usec = 0; | |
603 | setitimer(ITIMER_REAL, &itv, (struct itimerval *)NULL); | |
604 | /* It better not take a full minute to get to the read call */ | |
605 | ||
606 | while (charsWanted && (fTimeOut = setjmp(env)) == FALSE) | |
607 | { | |
608 | #ifdef ISOCONN | |
609 | got = SRead(conn, bptr, charsWanted, NOTOK); | |
610 | #else /* ISOCONN */ | |
611 | got = read(conn, bptr, charsWanted); | |
612 | #endif /* ISOCONN */ | |
613 | if (got <= 0) | |
614 | return FALSE; | |
615 | if(got > 0) | |
616 | { | |
617 | charsWanted -= got; | |
618 | bptr += got; | |
619 | /* Ok, we got something, reset the timer */ | |
620 | itv.it_value.tv_sec = TimeOutValue; | |
621 | itv.it_value.tv_usec = 0; | |
622 | setitimer(ITIMER_REAL, &itv, (struct itimerval *)NULL); | |
623 | } | |
624 | } | |
625 | /* disable the timer */ | |
626 | timerclear(&itv.it_value); | |
627 | setitimer(ITIMER_REAL, &itv, (struct itimerval *)NULL); | |
628 | /* If we got here and we didn't time out, then return TRUE, because | |
629 | * we must have read what we wanted. If we timed out, return FALSE */ | |
630 | if(fTimeOut && debug_conns) | |
631 | ErrorF("Timed out on connection %d\n", conn); | |
632 | return (!fTimeOut); | |
633 | } | |
634 | ||
635 | #ifdef ISOCONN | |
636 | /* | |
637 | * Who's calling us? | |
638 | */ | |
639 | getISOpeername (conn, from, alen) | |
640 | int conn; | |
641 | struct TSAPaddr *from; | |
642 | int *alen; | |
643 | { | |
644 | struct TSAPdisconnect td; | |
645 | if (TGetAddresses (conn, from, NULLTA, &td) == NOTOK) { | |
646 | Error(TErrString(td.td_reason)); | |
647 | return TRUE; | |
648 | }; | |
649 | *alen = sizeof(struct TSAPaddr); | |
650 | return FALSE; | |
651 | } | |
652 | #endif /* ISOCONN */ | |
653 | /***************************************************************** | |
654 | * ClientAuthorized | |
655 | * | |
656 | * Sent by the client at connection setup: | |
657 | * typedef struct _xConnClientPrefix { | |
658 | * CARD8 byteOrder; | |
659 | * BYTE pad; | |
660 | * CARD16 majorVersion, minorVersion; | |
661 | * CARD16 nbytesAuthProto; | |
662 | * CARD16 nbytesAuthString; | |
663 | * } xConnClientPrefix; | |
664 | * | |
665 | * It is hoped that eventually one protocol will be agreed upon. In the | |
666 | * mean time, a server that implements a different protocol than the | |
667 | * client expects, or a server that only implements the host-based | |
668 | * mechanism, will simply ignore this information. | |
669 | * | |
670 | *****************************************************************/ | |
671 | ||
672 | int | |
673 | ClientAuthorized(conn, pswapped, reason) | |
674 | long conn; | |
675 | int *pswapped; | |
676 | char **reason; /* if authorization fails, put reason in here */ | |
677 | { | |
678 | short slen; | |
679 | union { | |
680 | struct sockaddr sa; | |
681 | #ifdef UNIXCONN | |
682 | struct sockaddr_un un; | |
683 | #endif /* UNIXCONN */ | |
684 | #ifdef TCPCONN | |
685 | struct sockaddr_in in; | |
686 | #endif /* TCPCONN */ | |
687 | #ifdef DNETCONN | |
688 | struct sockaddr_dn dn; | |
689 | #endif /* DNETCONN */ | |
690 | #ifdef ISOCONN | |
691 | struct TSAPaddr ts; | |
692 | #endif /* ISOCONN */ | |
693 | } from; | |
694 | int fromlen; | |
695 | xConnClientPrefix xccp; | |
696 | char auth_proto[100]; | |
697 | char auth_string[100]; | |
698 | ||
699 | #ifdef ISOCONN | |
700 | /* | |
701 | * For now we always auth an ISO client!! | |
702 | * should use directory etc etc | |
703 | */ | |
704 | *reason = 0; | |
705 | #endif /* ISOCONN */ | |
706 | if (!ReadBuffer(conn, (char *)&xccp, sizeof(xConnClientPrefix))) | |
707 | { | |
708 | /* If they can't even give us this much, just blow them off | |
709 | * without an error message */ | |
710 | *reason = 0; | |
711 | return 0; | |
712 | } | |
713 | if (xccp.byteOrder != whichByteIsFirst) | |
714 | { | |
715 | SwapConnClientPrefix(&xccp); | |
716 | *pswapped = TRUE; | |
717 | } | |
718 | else | |
719 | *pswapped = FALSE; | |
720 | if ((xccp.majorVersion != X_PROTOCOL) || | |
721 | (xccp.minorVersion != X_PROTOCOL_REVISION)) | |
722 | { | |
723 | #define STR "Protocol version mismatch" | |
724 | *reason = (char *)xalloc(sizeof(STR)); | |
725 | strcpy(*reason, STR); | |
726 | if (debug_conns) | |
727 | ErrorF("%s\n", STR); | |
728 | #undef STR | |
729 | return 0; | |
730 | } | |
731 | fromlen = sizeof (from); | |
732 | #ifdef ISOCONN | |
733 | if (SGetPeerName (conn, &(from.ts), &fromlen) || | |
734 | InvalidHost (&(from.ts), fromlen)) | |
735 | #else /* ISOCONN */ | |
736 | if (getpeername (conn, &from.sa, &fromlen) || | |
737 | InvalidHost (&from.sa, fromlen)) | |
738 | #endif /* ISOCONN */ | |
739 | { | |
740 | #define STR "Server is not authorized to connect to host" | |
741 | *reason = (char *)xalloc(sizeof(STR)); | |
742 | strcpy(*reason, STR); | |
743 | #undef STR | |
744 | return 0; | |
745 | } | |
746 | ||
747 | slen = (xccp.nbytesAuthProto + 3) & ~3; | |
748 | if ( slen ) | |
749 | if (!ReadBuffer(conn, auth_proto, slen)) | |
750 | { | |
751 | #define STR "Length error in xConnClientPrefix for protocol authorization " | |
752 | *reason = (char *)xalloc(sizeof(STR)); | |
753 | strcpy(*reason, STR); | |
754 | return 0; | |
755 | #undef STR | |
756 | } | |
757 | auth_proto[slen] = '\0'; | |
758 | ||
759 | slen = (xccp.nbytesAuthString + 3) & ~3; | |
760 | if ( slen) | |
761 | if (!ReadBuffer(conn, auth_string, slen)) | |
762 | { | |
763 | #define STR "Length error in xConnClientPrefix for protocol string" | |
764 | *reason = (char *)xalloc(sizeof(STR)); | |
765 | strcpy(*reason, STR); | |
766 | return 0; | |
767 | #undef STR | |
768 | } | |
769 | auth_string[slen] = '\0'; | |
770 | ||
771 | /* At this point, if the client is authorized to change the access control | |
772 | * list, we should getpeername() information, and add the client to | |
773 | * the selfhosts list. It's not really the host machine, but the | |
774 | * true purpose of the selfhosts list is to see who may change the | |
775 | * access control list. | |
776 | */ | |
777 | return(1); | |
778 | } | |
779 | ||
780 | static int padlength[4] = {0, 3, 2, 1}; | |
781 | ||
782 | /***************** | |
783 | * EstablishNewConnections | |
784 | * If anyone is waiting on listened sockets, accept them. | |
785 | * Returns a mask with indices of new clients. Updates AllClients | |
786 | * and AllSockets. | |
787 | *****************/ | |
788 | ||
789 | void | |
790 | #ifdef ISOCONN | |
791 | EstablishNewConnections(newclients, nnew, vecp, vec) | |
792 | ClientPtr *newclients; | |
793 | int *nnew; | |
794 | int vecp; | |
795 | char **vec; | |
796 | #else /* ISOCONN */ | |
797 | EstablishNewConnections(newclients, nnew) | |
798 | ClientPtr *newclients; | |
799 | int *nnew; | |
800 | #endif /* ISOCONN */ | |
801 | { | |
802 | long readyconnections; /* mask of listeners that are ready */ | |
803 | long curconn; /* fd of listener that's ready */ | |
804 | long newconn; /* fd of new client */ | |
805 | int swapped; /* set by ClientAuthorized if connection is | |
806 | * swapped */ | |
807 | char *reason; | |
808 | struct iovec iov[2]; | |
809 | #ifdef ISOCONN | |
810 | struct udvec uv[3]; | |
811 | struct TSAPdisconnect tds; | |
812 | struct TSAPdisconnect *td = &tds; | |
813 | struct TSAPstart tsts; | |
814 | struct TSAPstart *tst = &tsts; | |
815 | #endif /* ISOCONN */ | |
816 | char p[3]; | |
817 | ||
818 | #ifdef TCP_NODELAY | |
819 | union { | |
820 | struct sockaddr sa; | |
821 | #ifdef UNIXCONN | |
822 | struct sockaddr_un un; | |
823 | #endif /* UNIXCONN */ | |
824 | #ifdef TCPCONN | |
825 | struct sockaddr_in in; | |
826 | #endif /* TCPCONN */ | |
827 | #ifdef DNETCONN | |
828 | struct sockaddr_dn dn; | |
829 | #endif /* DNETCONN */ | |
830 | } from; | |
831 | int fromlen; | |
832 | #endif TCP_NODELAY | |
833 | ||
834 | *nnew = 0; | |
835 | if (readyconnections = (LastSelectMask[0] & WellKnownConnections)) | |
836 | { | |
837 | while (readyconnections) | |
838 | { | |
839 | curconn = ffs (readyconnections) - 1; | |
840 | #ifdef ISOCONN | |
841 | /* | |
842 | * At this point, a TAccept has finished with a vec > 0 | |
843 | * so we need to init them... | |
844 | */ | |
845 | if ((newconn = SAccept(curconn, vecp, vec)) >= 0) | |
846 | { | |
847 | fd2family[newconn] = ISODE_IO; | |
848 | #else /* ISOCONN */ | |
849 | if ((newconn = accept (curconn, | |
850 | (struct sockaddr *) NULL, | |
851 | (int *)NULL)) >= 0) | |
852 | { | |
853 | fd2family[newconn] = UNIX_IO; | |
854 | #endif /* ISOCONN */ | |
855 | if (newconn >= lastfdesc) | |
856 | { | |
857 | if (debug_conns) | |
858 | ErrorF("Didn't make connection: Out of file descriptors for connections\n"); | |
859 | #ifdef ISOCONN | |
860 | SClose(newconn); | |
861 | #else /* ISOCONN */ | |
862 | close (newconn); | |
863 | #endif /* ISOCONN */ | |
864 | } | |
865 | else | |
866 | { | |
867 | ClientPtr next = (ClientPtr)NULL; | |
868 | ||
869 | #ifdef TCP_NODELAY | |
870 | #ifdef ISOCONN | |
871 | if (fd2family(newconn) == UNIX_IO) | |
872 | { | |
873 | #endif /* ISOCONN */ | |
874 | fromlen = sizeof (from); | |
875 | if (!getpeername (newconn, &from.sa, &fromlen)) | |
876 | { | |
877 | if (fromlen && (from.sa.sa_family == AF_INET)) | |
878 | { | |
879 | int mi = 1; | |
880 | setsockopt (newconn, IPPROTO_TCP, TCP_NODELAY, | |
881 | (char *)&mi, sizeof (int)); | |
882 | } | |
883 | } | |
884 | #ifdef ISOCONN | |
885 | } | |
886 | #endif /* ISOCONN */ | |
887 | #endif /* TCP_NODELAY */ | |
888 | if (ClientAuthorized(newconn, &swapped, &reason)) | |
889 | { | |
890 | #ifdef hpux | |
891 | /* | |
892 | * HPUX does not have FNDELAY | |
893 | */ | |
894 | { | |
895 | int arg; | |
896 | arg = 1; | |
897 | ioctl(newconn, FIOSNBIO, &arg); | |
898 | } | |
899 | #else | |
900 | fcntl (newconn, F_SETFL, FNDELAY); | |
901 | #endif /* hpux */ | |
902 | inputBuffers[newconn].used = 1; | |
903 | if (! inputBuffers[newconn].size) | |
904 | { | |
905 | inputBuffers[newconn].buffer = | |
906 | (char *)xalloc(BUFSIZE); | |
907 | inputBuffers[newconn].size = BUFSIZE; | |
908 | inputBuffers[newconn].bufptr = | |
909 | inputBuffers[newconn].buffer; | |
910 | } | |
911 | if (GrabDone) | |
912 | { | |
913 | BITSET(SavedAllClients, newconn); | |
914 | BITSET(SavedAllSockets, newconn); | |
915 | } | |
916 | else | |
917 | { | |
918 | BITSET(AllClients, newconn); | |
919 | BITSET(AllSockets, newconn); | |
920 | } | |
921 | next = NextAvailableClient(); | |
922 | if (next != (ClientPtr)NULL) | |
923 | { | |
924 | OsCommPtr priv; | |
925 | ||
926 | newclients[(*nnew)++] = next; | |
927 | next->swapped = swapped; | |
928 | ConnectionTranslation[newconn] = next; | |
929 | priv = (OsCommPtr)xalloc(sizeof(OsCommRec)); | |
930 | priv->fd = newconn; | |
931 | priv->buf = (unsigned char *) | |
932 | xalloc(OutputBufferSize); | |
933 | priv->bufsize = OutputBufferSize; | |
934 | priv->count = 0; | |
935 | next->osPrivate = (pointer)priv; | |
936 | } | |
937 | else | |
938 | { | |
939 | #define STR "Maximum number of clients exceeded" | |
940 | reason = (char *)xalloc(sizeof(STR)); | |
941 | strcpy(reason, STR); | |
942 | #undef STR | |
943 | } | |
944 | } | |
945 | if (next == (ClientPtr)NULL) | |
946 | { | |
947 | xConnSetupPrefix c; | |
948 | ||
949 | if(reason) | |
950 | { | |
951 | c.success = xFalse; | |
952 | c.lengthReason = strlen(reason); | |
953 | c.length = (c.lengthReason + 3) >> 2; | |
954 | c.majorVersion = X_PROTOCOL; | |
955 | c.minorVersion = X_PROTOCOL_REVISION; | |
956 | if(swapped) | |
957 | { | |
958 | int n; | |
959 | ||
960 | swaps(&c.majorVersion, n); | |
961 | swaps(&c.minorVersion, n); | |
962 | swaps(&c.length, n); | |
963 | } | |
964 | ||
965 | #ifdef ISOCONN | |
966 | (void)SWrite(newconn, (char *)&c, sizeof(xConnSetupPrefix)); | |
967 | #else /* ISOCONN */ | |
968 | write(newconn, (char *)&c, sizeof(xConnSetupPrefix)); | |
969 | #endif /* ISOCONN */ | |
970 | iov[0].iov_len = c.lengthReason; | |
971 | iov[0].iov_base = reason; | |
972 | iov[1].iov_len = padlength[c.lengthReason & 3]; | |
973 | iov[1].iov_base = p; | |
974 | #ifdef ISOCONN | |
975 | SWritev(newconn, iov, 2); | |
976 | #else /* ISOCONN */ | |
977 | writev(newconn, iov, 2); | |
978 | #endif /* ISOCONN */ | |
979 | if (debug_conns) | |
980 | ErrorF("Didn't make connection:%s\n", reason); | |
981 | } | |
982 | #ifdef ISOCONN | |
983 | SClose(newconn); | |
984 | #else /* ISOCONN */ | |
985 | close(newconn); | |
986 | #endif /* ISOCONN */ | |
987 | xfree(reason); | |
988 | } | |
989 | ||
990 | } | |
991 | } | |
992 | readyconnections &= ~(1 << curconn); | |
993 | } | |
994 | } | |
995 | } | |
996 | ||
997 | /************ | |
998 | * CloseDownFileDescriptor: | |
999 | * Remove this file descriptor and it's inputbuffers, etc. | |
1000 | ************/ | |
1001 | ||
1002 | void | |
1003 | CloseDownFileDescriptor(connection) | |
1004 | int connection; | |
1005 | { | |
1006 | #ifdef ISOCONN | |
1007 | struct TSAPdisconnect tds; | |
1008 | #ifdef ISODEBUG | |
1009 | if (isodexbug) | |
1010 | fprintf(stderr, "server: TDiscReq\n"); | |
1011 | #endif /* ISODEBUG */ | |
1012 | SClose(connection); | |
1013 | #else /* ISOCONN */ | |
1014 | close(connection); | |
1015 | #endif /* ISOCONN */ | |
1016 | ||
1017 | if (inputBuffers[connection].size) | |
1018 | { | |
1019 | xfree(inputBuffers[connection].buffer); | |
1020 | inputBuffers[connection].buffer = (char *) NULL; | |
1021 | inputBuffers[connection].bufptr = (char *) NULL; | |
1022 | inputBuffers[connection].size = 0; | |
1023 | } | |
1024 | inputBuffers[connection].bufcnt = 0; | |
1025 | inputBuffers[connection].lenLastReq = 0; | |
1026 | inputBuffers[connection].used = 0; | |
1027 | ||
1028 | BITCLEAR(AllSockets, connection); | |
1029 | BITCLEAR(AllClients, connection); | |
1030 | BITCLEAR(ClientsWithInput, connection); | |
1031 | BITCLEAR(ClientsWriteBlocked, connection); | |
1032 | if (!ANYSET(ClientsWriteBlocked)) | |
1033 | AnyClientsWriteBlocked = FALSE; | |
1034 | } | |
1035 | ||
1036 | /***************** | |
1037 | * CheckConections | |
1038 | * Some connection has died, go find which one and shut it down | |
1039 | * The file descriptor has been closed, but is still in AllClients. | |
1040 | * If would truly be wonderful if select() would put the bogus | |
1041 | * file descriptors in the exception mask, but nooooo. So we have | |
1042 | * to check each and every socket individually. | |
1043 | *****************/ | |
1044 | ||
1045 | void | |
1046 | CheckConnections() | |
1047 | { | |
1048 | long mask[mskcnt]; | |
1049 | long tmask[mskcnt]; | |
1050 | register int curclient; | |
1051 | int i; | |
1052 | struct timeval notime; | |
1053 | ClientPtr bad; | |
1054 | int r; | |
1055 | #ifdef ISOCONN | |
1056 | struct TSAPdisconnect tds; | |
1057 | struct TSAPdisconnect *td = &tds; | |
1058 | char *vec[4]; | |
1059 | int vecp; | |
1060 | #endif /* ISOCONN */ | |
1061 | ||
1062 | notime.tv_sec = 0; | |
1063 | notime.tv_usec = 0; | |
1064 | ||
1065 | COPYBITS(AllClients, mask); | |
1066 | for (i=0; i<mskcnt; i++) | |
1067 | { | |
1068 | while (mask[i]) | |
1069 | { | |
1070 | curclient = ffs (mask[i]) - 1 + (i << 5); | |
1071 | CLEARBITS(tmask); | |
1072 | BITSET(tmask, curclient); | |
1073 | #ifdef ISOCONN | |
1074 | r = TNetAccept (&vecp, vec, | |
1075 | curclient + 1, tmask, (int *)NULL, (int *)NULL, | |
1076 | OK, td); | |
1077 | if (r == NOTOK) | |
1078 | { | |
1079 | Error(TErrString(td->td_reason)); | |
1080 | Error("TNetAccept"); | |
1081 | #else /* ISOCONN */ | |
1082 | r = select (curclient + 1, tmask, (int *)NULL, (int *)NULL, | |
1083 | ¬ime); | |
1084 | if (r < 0) | |
1085 | { | |
1086 | #endif /* ISOCONN */ | |
1087 | if (bad = ConnectionTranslation[curclient]) | |
1088 | CloseDownClient(bad); | |
1089 | else | |
1090 | CloseDownFileDescriptor(curclient); | |
1091 | } | |
1092 | BITCLEAR(mask, curclient); | |
1093 | } | |
1094 | } | |
1095 | } | |
1096 | ||
1097 | ||
1098 | /***************** | |
1099 | * CloseDownConnection | |
1100 | * Delete client from AllClients and free resources | |
1101 | *****************/ | |
1102 | ||
1103 | CloseDownConnection(client) | |
1104 | ClientPtr client; | |
1105 | { | |
1106 | OsCommPtr oc = (OsCommPtr)client->osPrivate; | |
1107 | ||
1108 | ConnectionTranslation[oc->fd] = (ClientPtr)NULL; | |
1109 | CloseDownFileDescriptor(oc->fd); | |
1110 | if (oc->buf != NULL) /* an Xrealloc may have returned NULL */ | |
1111 | xfree(oc->buf); | |
1112 | xfree(client->osPrivate); | |
1113 | } | |
1114 | ||
1115 | ||
1116 | AddEnabledDevice(fd) | |
1117 | int fd; | |
1118 | { | |
1119 | EnabledDevices |= (1<<fd); | |
1120 | BITSET(AllSockets, fd); | |
1121 | } | |
1122 | ||
1123 | ||
1124 | RemoveEnabledDevice(fd) | |
1125 | int fd; | |
1126 | { | |
1127 | EnabledDevices &= ~(1<<fd); | |
1128 | BITCLEAR(AllSockets, fd); | |
1129 | } | |
1130 | ||
1131 | /***************** | |
1132 | * OnlyListenToOneClient: | |
1133 | * Only accept requests from one client. Continue to handle new | |
1134 | * connections, but don't take any protocol requests from the new | |
1135 | * ones. Note that if GrabDone is set, EstablishNewConnections | |
1136 | * needs to put new clients into SavedAllSockets and SavedAllClients. | |
1137 | * Note also that there is no timeout for this in the protocol. | |
1138 | * This routine is "undone" by ListenToAllClients() | |
1139 | *****************/ | |
1140 | ||
1141 | OnlyListenToOneClient(client) | |
1142 | ClientPtr client; | |
1143 | { | |
1144 | OsCommPtr oc = (OsCommPtr)client->osPrivate; | |
1145 | int connection = oc->fd; | |
1146 | ||
1147 | if (! GrabDone) | |
1148 | { | |
1149 | COPYBITS (ClientsWithInput, SavedClientsWithInput); | |
1150 | BITCLEAR (SavedClientsWithInput, connection); | |
1151 | if (GETBIT(ClientsWithInput, connection)) | |
1152 | { | |
1153 | CLEARBITS(ClientsWithInput); | |
1154 | BITSET(ClientsWithInput, connection); | |
1155 | } | |
1156 | else | |
1157 | { | |
1158 | CLEARBITS(ClientsWithInput); | |
1159 | } | |
1160 | COPYBITS(AllSockets, SavedAllSockets); | |
1161 | COPYBITS(AllClients, SavedAllClients); | |
1162 | ||
1163 | UNSETBITS(AllSockets, AllClients); | |
1164 | BITSET(AllSockets, connection); | |
1165 | CLEARBITS(AllClients); | |
1166 | BITSET(AllClients, connection); | |
1167 | GrabDone = TRUE; | |
1168 | } | |
1169 | } | |
1170 | ||
1171 | /**************** | |
1172 | * ListenToAllClients: | |
1173 | * Undoes OnlyListentToOneClient() | |
1174 | ****************/ | |
1175 | ||
1176 | ListenToAllClients() | |
1177 | { | |
1178 | if (GrabDone) | |
1179 | { | |
1180 | ORBITS(AllSockets, AllSockets, SavedAllSockets); | |
1181 | ORBITS(AllClients, AllClients, SavedAllClients); | |
1182 | ORBITS(ClientsWithInput, ClientsWithInput, SavedClientsWithInput); | |
1183 | GrabDone = FALSE; | |
1184 | } | |
1185 | } | |
1186 | ||
1187 |