source: trunk/athena/bin/discuss/libds/rpcall.c @ 23920

Revision 23920, 11.2 KB checked in by broder, 15 years ago (diff)
In discuss: * Fix local/dumb authentication needed for mail delivery and local client usage. Patch by Mitch Berger. (Trac: #274)
Line 
1/*
2 *
3 *      Copyright (C) 1988, 1989 by the Massachusetts Institute of Technology
4 *      Developed by the MIT Student Information Processing Board (SIPB).
5 *      For copying information, see the file mit-copyright.h in this release.
6 *
7 */
8/*
9 *
10 *  rpcall.c -- Procedures to implement a simple (perhaps brain-asleep) RPC
11 *              protocol over a TCP connection.
12 *              This file handles the caller's side of the connection.
13 *
14 *      $Id: rpcall.c,v 1.25 2007-08-09 20:41:32 amb Exp $
15 *
16 */
17#ifndef lint
18static char rcsid_rpcall_c[] =
19    "$Id: rpcall.c,v 1.25 2007-08-09 20:41:32 amb Exp $";
20#endif /* lint */
21
22/* INCLUDES */
23
24#include <sys/types.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <ctype.h>
28#include <string.h>
29#include <fcntl.h>
30#include <errno.h>
31#include <sys/time.h>
32#include <sys/socket.h>
33#include <netinet/in.h>
34#include <netdb.h>
35#if HAVE_FCNTL_H
36#include <fcntl.h>
37#endif
38#include <discuss/tfile.h>
39#include "rpc.h"
40#include "config.h"
41
42#ifdef NOKERBEROS
43#undef HAVE_KRB4
44#undef HAVE_KRB5
45#endif /* NOKERBEROS */
46
47#ifdef HAVE_KRB5
48#include "krb5.h"
49#endif /* HAVE_KRB5 */
50#include "rpc_et.h"
51
52/* DEFINES */
53
54#define min(A, B) ((A) < (B) ? (A) : (B))
55#define SUCCESS 1
56#define ERROR   -1
57
58/* EXTERNAL ROUTINES */
59
60int rpc_err;
61
62/* static variables and functions */
63
64/* panic -- just a printf */
65static void panic(str)
66    char *str;
67{
68    fprintf(stderr, "panic: %s\n", str);
69    perror("discuss");
70    exit(1);
71}
72
73/* argument list info */
74static int procno;                              /* procedure number */
75
76/* connections & socket info */
77static USPStream *us = NULL;
78
79/*
80 *
81 * startsend()  -- Get ready for an RPC call.
82 *
83 */
84void startsend(whichproc)
85    int whichproc;
86{
87    procno = whichproc;
88    if (us == NULL) {
89        rpc_err = RPC_NOT_INIT;
90        return;
91    }
92    USP_begin_block(us,PROC_BASE+procno);
93    rpc_err = 0;
94
95    return;
96}
97
98/*
99 *
100 * sendint(i)  -- Send an integer in an RPC call.
101 *
102 */
103void sendint(i)
104    int i;
105{
106    if (USP_put_long_integer(us, i) != SUCCESS) {
107        rpc_err = errno;
108    }
109}
110/*
111 *
112 * sendshort(i)  -- Send a short integer in an RPC call.
113 *
114 */
115void sendshort(i)
116    short i;
117{
118    if (USP_put_integer(us, i) != SUCCESS) {
119        rpc_err = errno;
120    }
121}
122
123/*
124 *
125 * sendstr(i)  -- Send a string in an RPC call.
126 *
127 */
128void sendstr(str)
129    char *str;
130{
131    if (us == NULL) {
132        rpc_err = RPC_NOT_INIT;
133        return;
134    }
135    if (USP_put_string(us, str) != SUCCESS) {
136        rpc_err = errno;
137    }
138}
139
140/*
141 *
142 * sendbool(b)  -- Send a boolean in an RPC call.
143 *
144 */
145void sendbool(b)
146    unsigned short b;
147{
148    if (USP_put_boolean(us, b) != SUCCESS) {
149        rpc_err = errno;
150    }
151}
152
153
154/*
155 *
156 * rpc_sendfile(tf)  -- Send a file in an RPC call.
157 *
158 */
159void rpc_sendfile(tf)
160    tfile tf;
161{
162    int tfs;
163
164    tfs = tfsize (tf);
165    if (USP_put_long_integer(us, tfs) != SUCCESS) {
166        rpc_err = errno;
167    }
168}
169
170/*
171 *
172 * sendit () -- Make the final call.
173 *
174 */
175void sendit(dest)
176    char *dest;
177{
178    if (USP_end_block(us) != SUCCESS) {
179        rpc_err = errno;
180    }
181    return;
182}
183
184/*
185 *
186 * init_rpc () -- Initialize the RPC mechanism
187 *
188 */
189void init_rpc ()
190{
191#if defined(__APPLE__) && defined(__MACH__)
192    add_error_table(&et_rpc_error_table);
193    add_error_table(&et_usp_error_table);
194#else
195    initialize_rpc_error_table();
196    initialize_usp_error_table();
197#endif
198}
199
200/*
201 *
202 * term_rpc -- Shutdown the rpc mechanism
203 *
204 */
205void term_rpc()
206{
207    flush_convs ();
208    return;
209}
210
211/*
212 *
213 * close_rpc () -- Close down a specific rpc conversation
214 *
215 */
216void close_rpc(rc)
217    rpc_conversation rc;
218{
219    USP_close_connection(rc);
220    us = NULL;
221    return;
222}
223
224/*
225 *
226 * set_rpc ()  -- Sets the current rpc conversation
227 *
228 */
229void set_rpc(rc)
230    rpc_conversation rc;
231{
232    us = rc;
233}
234
235/*
236 *
237 * open_rpc ()  -- Open the connection to the server
238 *                 Returns an rpc conversation id.
239 *
240 */
241rpc_conversation open_rpc (host, port_num, service_id, code)
242    char *host;                 /* hostname to connect to */
243    int port_num;               /* port number to use */
244    char *service_id;           /* authenticator service id */
245    register int *code;         /* return code */
246{
247    int parent,sv[2];
248    rpc_conversation conv;
249    struct hostent *hp;
250    int authl;
251    register int i, s = -1;
252
253    char *server_name,*authp;
254    struct sockaddr_in address, peeraddr;
255    int retval, flags, arglen;
256
257    *code = 0;
258
259    if (service_id [0] == '/') { /* authenticate using sub-process */
260        if (socketpair(AF_UNIX,SOCK_STREAM,0,sv) < 0)
261            panic ("can't do socket pair");
262
263        parent = fork ();
264        if (parent < 0)
265            panic ("Can't fork");
266        if (!parent) {          /* child's play */
267            dup2(sv[1],0);      /* child takes second one */
268
269            for (i = 3; i < 20; i++)
270                (void) close (i);
271
272            server_name = strrchr (service_id, '/');
273            if (server_name == NULL)
274                server_name = service_id;
275            else
276                server_name++;
277            execl(service_id, server_name, 0);
278            {
279                char buf[100];
280                sprintf(buf, "Can't exec %s", service_id);
281                panic (buf);
282            }
283        } else {
284            (void) close (sv[1]);
285            (void) fcntl (sv[0], F_SETFD, 1);
286            us = USP_associate (sv[0]);
287            return(us);
288        }
289    }
290
291    hp = gethostbyname(host);
292    if (hp == NULL) {
293        extern int h_errno;
294        int h = h_errno;
295        switch (h) {
296        case HOST_NOT_FOUND:
297            *code = RPC_HOST_UNKNOWN;
298            break;
299        case TRY_AGAIN:
300            *code = RPC_NS_TIMEOUT;
301            break;
302        case NO_RECOVERY:
303            *code = RPC_NS_ERROR;
304            break;
305        case NO_ADDRESS:
306            *code = RPC_NO_ADDR;
307            break;
308        default:
309            *code = RPC_NS_ERROR;
310            break;
311        }
312        return(NULL);
313    }
314
315    /* since we already have our port number, the following code was
316       stolen from USP to manually set up the connection.  Note that
317       one benefit from using the primitive USP routine (USP_associate)
318       is that we eliminate an extra host lookup, and we don't have
319       to rely on USP's primitive error mechanism.  Yeah! */
320
321    memset(&address, 0, sizeof(address));
322    memcpy(&address.sin_addr, hp->h_addr, hp->h_length);
323    address.sin_family = hp->h_addrtype;
324    address.sin_port = port_num;
325    if((s = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0)
326        goto punt;
327
328    /* attempt to connect to the remote host, timing out after a
329       pre-configured period of time.. */
330    flags = fcntl(s, F_GETFL);
331    if (flags < 0)
332        goto punt;
333
334    /* put socket in non-blocking mode */
335    if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
336        goto punt;
337
338    retval = connect(s, (struct sockaddr *) &address, sizeof(address));
339    /* if connect returns an error immediately, punt */
340    if (retval < 0 && errno != EINPROGRESS)
341        goto punt;
342
343    /* if not connected immediately, wait until connected or timed out */
344    if (retval < 0) {
345        struct timeval conn_timeout;
346        fd_set fds;
347        char c;
348
349        conn_timeout.tv_sec = CONN_TIMEOUT;
350        conn_timeout.tv_usec = 0;
351
352        FD_ZERO(&fds);
353        FD_SET(s, &fds);
354
355        retval = select(s + 1, NULL, &fds, NULL, &conn_timeout);
356
357        /* if select returned an error, punt */
358        if (retval < 0)
359            goto punt;
360
361        /* if no response, set errno and punt */
362        if (retval == 0) {
363            errno = ETIMEDOUT;
364            goto punt;
365        }
366
367        /* presumably got a response; check if the connection is open */
368        arglen = sizeof(peeraddr);
369        if (getpeername(s, (struct sockaddr *)&peeraddr, &arglen) < 0) {
370            /* not connected; find out why and punt */
371            arglen = sizeof(errno);
372            (void) getsockopt(s, SOL_SOCKET, SO_ERROR, (void *)&errno, &arglen);
373            goto punt;
374        }
375    }
376
377    /* put socket back in blocking mode */
378    if (fcntl(s, F_SETFL, flags) < 0)
379        goto punt;
380
381    (void) fcntl (s, F_SETFD, 1);
382    conv = USP_associate (s);
383    us = conv;
384    if (!us)
385        goto punt;
386
387    get_authenticator(service_id, 0, &authp, &authl, code);
388    if (! *code) {
389        USP_begin_block(us,KRB_TICKET);
390        sendshort(authl);
391        for (i = 0; i < authl; i++) {
392            sendshort(*authp++);
393        }
394        USP_end_block(us);
395#ifdef HAVE_KRB5
396        /* Prior to server version 3, Kerberos 5 wasn't an available
397         * authentication method, so we need to send a Kerberos 4 ticket.
398         * Unfortunately, there's no way to query the server version sooner. */
399        krb5_error_code kcode;
400        krb5_principal princ;
401        char name[30];
402        char inst[100];
403        char realm[30];
404        krb5_context context;
405        if (get_server_version() < SERVER_3) {
406#ifdef HAVE_KRB4
407            kcode = krb5_init_context(&context);
408            if (kcode) {
409                com_err("discuss", kcode, "while initializing krb5");
410                return(conv);
411            }
412            kcode = krb5_parse_name(context, service_id, &princ);
413            if (kcode) {
414                com_err("discuss", kcode, "while parsing krb5 name");
415                return(conv);
416            }
417            kcode = krb5_524_conv_principal(context, princ, name, inst, realm);
418            if (kcode) {
419                com_err("discuss", kcode, "while converting k5 princ to k4");
420                return(conv);
421            }
422            strcpy(service_id, name);
423            if (*inst) {
424                strcat(service_id, ".");
425                strcat(service_id, inst);
426            }
427            strcat(service_id, "@");
428            strcat(service_id, realm);
429            get_authenticator_krb4(service_id, 0, &authp, &authl, code);
430            if (! *code) {
431                USP_begin_block(us,KRB_TICKET);
432                sendshort(authl);
433                for (i = 0; i < authl; i++) {
434                    sendshort(*authp++);
435                }
436                USP_end_block(us);
437            }
438#else /* HAVE_KRB4 */
439            com_err("discuss", RPC_SERVER_TOO_OLD, "while authenticating to discuss server");
440#endif /* HAVE_KRB4 */
441        }
442#endif /* HAVE_KRB5 */
443    } else {
444        USP_begin_block(us,KRB_TICKET); /* send blank ticket */
445        sendshort(0);
446        USP_end_block(us);
447    }
448    return(conv);
449punt:
450    if (s >= 0) close(s);
451    *code = errno;
452    return(NULL);
453}
454
455/*
456 *
457 * recvreply ()  -- Routine to accept an RPC return.
458 *
459 */
460void recvreply ()
461{
462    USPCardinal bt;
463
464    if (USP_rcv_blk(us, &bt) != SUCCESS) {
465        rpc_err = errno;
466        return;
467    }
468
469    if (bt != REPLY_TYPE) {
470        if (bt == UNKNOWN_CALL)
471            rpc_err = RPC_UNIMPL_CALL;
472        else
473            rpc_err = RPC_PROTOCOL;
474        USP_flush_block(us);
475    }
476    return;
477
478}
479
480int recvint ()
481{
482    USPLong_integer li;
483
484    if (USP_get_long_integer(us, &li) != SUCCESS) {
485        rpc_err = errno;
486        return(0);
487    }
488
489    return (li);
490}
491
492/*
493 *
494 * recvstr ()  -- Receive a string from an RPC call
495 *
496 */
497
498char *recvstr ()
499{
500    USPString str;
501
502    if (USP_get_string(us, &str) != SUCCESS) {
503        rpc_err = errno;
504        return("");
505    }
506
507    return (str);
508}
509unsigned short recvbool()
510{
511    USPBoolean flag;
512
513    if (USP_get_boolean(us, &flag) != SUCCESS) {
514        rpc_err = errno;
515        return(0);
516    }
517
518    return (flag);
519}
520
521/*
522 *
523 * senddata () -- Routine to send the contents of a transaction file
524 *                across the net.
525 *
526 */
527void senddata(tf)
528    tfile tf;
529{
530    int tfs,tocopy;
531    char buffer[508];
532    int result;
533
534    topen (tf, "r", &result);
535    USP_begin_block (us, TFILE_BLK);
536    tfs = tfsize(tf);
537    while (tfs > 0) {
538        tocopy = min (tfs, 508);
539        tread (tf, buffer, tocopy, &result);
540        if (result)
541            break;
542        USP_put_byte_block(us, buffer, tocopy);
543        tfs -= tocopy;
544    }
545    USP_end_block(us);
546    tclose (tf, &result);
547}
548
549/*
550 *
551 * recvdata () -- Routine to receive a USP file.
552 *
553 */
554void recvdata(tf)
555    tfile tf;
556{
557    char buffer[508];
558    USPCardinal bt;
559    unsigned actual;
560    int result;
561
562    if (USP_rcv_blk(us, &bt) != SUCCESS) {
563        rpc_err = errno;
564        return;
565    }
566
567    if (bt != TFILE_BLK) {
568        rpc_err = RPC_PROTOCOL;
569        return;
570    }
571
572    topen (tf, "w", &result);
573    if (result) goto done;
574    for (;;) {
575        if (USP_get_byte_block(us, buffer, 508, &actual) != SUCCESS) {
576            rpc_err = errno;
577            break;
578        }
579        if (actual == 0)
580            break;
581        twrite(tf, buffer, actual, &result);
582        if (result) break;
583    }
584
585    tclose (tf, &result);
586
587done:
588    USP_flush_block(us);
589}
Note: See TracBrowser for help on using the repository browser.