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

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