root/common/trunk/src/mbus.c @ 141

Revision 141, 34.3 KB (checked in by ucaccsp, 15 years ago)

Updates to get rid of the ERANGE trouble (well, to work around it really).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * FILE:    mbus.c
3 * AUTHORS: Colin Perkins
4 *
5 * Copyright (c) 1997-99 University College London
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, is permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *      This product includes software developed by the Computer Science
19 *      Department at University College London
20 * 4. Neither the name of the University nor of the Department may be used
21 *    to endorse or promote products derived from this software without
22 *    specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#define MBUS_ENCRYPT_BY_DEFAULT
37
38#include "config_unix.h"
39#include "config_win32.h"
40#include "debug.h"
41#include "memory.h"
42#include "net_udp.h"
43#include "hmac.h"
44#include "base64.h"
45#include "crypt_random.h"
46#include "qfDES.h"
47#include "mbus.h"
48
49#define MBUS_BUF_SIZE     1500
50#define MBUS_ACK_BUF_SIZE 1500
51#define MBUS_MAX_ADDR       10
52#define MBUS_MAX_PD         10
53#define MBUS_MAX_QLEN       50 /* Number of messages we can queue with mbus_qmsg() */
54
55struct mbus_key{
56        char    *algorithm;
57        char    *key;
58        int      key_len;
59};
60
61struct mbus_msg {
62        struct mbus_msg *next;
63        struct timeval   time;  /* Time the message was sent, to trigger a retransmit */
64        struct timeval   ts;    /* Time the message was composed, the timestamp in the packet header */
65        char            *dest;
66        int              reliable;
67        int              seqnum;
68        int              retransmit_count;
69        int              message_size;
70        int              num_cmds;
71        char            *cmd_list[MBUS_MAX_QLEN];
72        char            *arg_list[MBUS_MAX_QLEN];
73};
74
75struct mbus {
76        socket_udp       *s;
77        int               num_addr;
78        char             *addr[MBUS_MAX_ADDR];  /* Addresses we respond to.                     */
79        int               max_other_addr;
80        int               num_other_addr;
81        char            **other_addr;           /* Addresses of other entities on the mbus.     */
82        char             *parse_buffer[MBUS_MAX_PD];
83        int               parse_depth;
84        int               seqnum;
85        void (*cmd_handler)(char *src, char *cmd, char *arg, void *dat);
86        void (*err_handler)(int seqnum, int reason);
87        struct mbus_msg  *cmd_queue;            /* Queue of messages waiting to be sent */
88        struct mbus_msg  *waiting_ack;          /* The last reliable message sent, if we have not yet got the ACK */
89        char             *hashkey;
90        int               hashkeylen;
91        char             *encrkey;
92        int               encrkeylen;
93#ifdef WIN32
94        HKEY              cfgKey;
95#else
96        fd_t              cfgfd;                /* The file descriptor for the $HOME/.mbus config file, on Unix */
97#endif
98        int               cfg_locked;
99        struct timeval    last_heartbeat;       /* Last time we sent a heartbeat message */
100};
101
102#define SECS_PER_WEEK    604800
103#define MBUS_ENCRKEY_LEN      7
104#define MBUS_HASHKEY_LEN     12
105
106static char *mbus_new_encrkey(void)
107{
108        char            *key;   /* The key we are going to return... */
109#ifdef MBUS_ENCRYPT_BY_DEFAULT
110        /* Create a new key, for use by the hashing routines. Returns */
111        /* a key of the form (DES,MTIzMTU2MTg5MTEyMQ==)               */
112        char             random_string[MBUS_ENCRKEY_LEN];
113        char             encoded_string[(MBUS_ENCRKEY_LEN*4/3)+4];
114        int              encoded_length;
115        int              i;
116
117        /* Step 1: generate a random string for the key... */
118        for (i = 0; i < MBUS_ENCRKEY_LEN; i++) {
119                random_string[i] = ((int32)lbl_random() | 0x000ff000) >> 24;
120        }
121        /* Step 2: base64 encode that string... */
122        memset(encoded_string, 0, (MBUS_ENCRKEY_LEN*4/3)+4);
123        encoded_length = base64encode(random_string, MBUS_ENCRKEY_LEN, encoded_string, (MBUS_ENCRKEY_LEN*4/3)+4);
124
125        /* Step 3: put it all together to produce the key... */
126        key = (char *) xmalloc(encoded_length + 18);
127        sprintf(key, "(DES,%s)", encoded_string);
128#else
129        key = (char *) xmalloc(9);
130        sprintf(key, "(NOENCR)");
131#endif
132        return key;
133}
134
135static char *mbus_new_hashkey(void)
136{
137        /* Create a new key, for use by the hashing routines. Returns  */
138        /* a key of the form (HMAC-MD5,MTIzMTU2MTg5MTEyMQ==)           */
139        char             random_string[MBUS_HASHKEY_LEN];
140        char             encoded_string[(MBUS_HASHKEY_LEN*4/3)+4];
141        int              encoded_length;
142        int              i;
143        char            *key;
144
145        /* Step 1: generate a random string for the key... */
146        for (i = 0; i < MBUS_HASHKEY_LEN; i++) {
147                random_string[i] = ((int32)lbl_random() | 0x000ff000) >> 24;
148        }
149        /* Step 2: base64 encode that string... */
150        memset(encoded_string, 0, (MBUS_HASHKEY_LEN*4/3)+4);
151        encoded_length = base64encode(random_string, MBUS_HASHKEY_LEN, encoded_string, (MBUS_HASHKEY_LEN*4/3)+4);
152
153        /* Step 3: put it all together to produce the key... */
154        key = (char *) xmalloc(encoded_length + 23);
155        sprintf(key, "(HMAC-MD5,%s)", encoded_string);
156
157        return key;
158}
159
160static void mbus_lock_config_file(struct mbus *m)
161{
162#ifdef WIN32
163        /* Open the registry and create the mbus entries if they don't exist   */
164        /* already. The default contents of the registry are random encryption */
165        /* and authentication keys, and node local scope.                      */
166        HKEY                    key    = HKEY_CURRENT_USER;
167        LPCTSTR                 subKey = "Software\\Mbone Applications\\mbus";
168        DWORD                   disp;
169        char                    buffer[MBUS_BUF_SIZE];
170        LONG                    status;
171
172        status = RegCreateKeyEx(key, subKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &(m->cfgKey), &disp);
173        if (status != ERROR_SUCCESS) {
174                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, MBUS_BUF_SIZE, NULL);
175                debug_msg("Unable to open registry: %s\n", buffer);
176                abort();
177        }
178        if (disp == REG_CREATED_NEW_KEY) {
179                char    *hashkey = mbus_new_hashkey();
180                char    *encrkey = mbus_new_encrkey();
181                char    *scope   = "HOSTLOCAL";
182
183                status = RegSetValueEx(m->cfgKey, "HASHKEY", 0, REG_SZ, hashkey, strlen(hashkey) + 1);
184                if (status != ERROR_SUCCESS) {
185                        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, MBUS_BUF_SIZE, NULL);
186                        debug_msg("Unable to set hashkey: %s\n", buffer);
187                        abort();
188                }       
189                status = RegSetValueEx(m->cfgKey, "ENCRYPTIONKEY", 0, REG_SZ, encrkey, strlen(encrkey) + 1);
190                if (status != ERROR_SUCCESS) {
191                        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, MBUS_BUF_SIZE, NULL);
192                        debug_msg("Unable to set encrkey: %s\n", buffer);
193                        abort();
194                }       
195                status = RegSetValueEx(m->cfgKey, "SCOPE", 0, REG_SZ, scope, strlen(scope) + 1);
196                if (status != ERROR_SUCCESS) {
197                        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, MBUS_BUF_SIZE, NULL);
198                        debug_msg("Unable to set scope: %s\n", buffer);
199                        abort();
200                }       
201                debug_msg("Created new registry entry...\n");
202        } else {
203                debug_msg("Opened existing registry entry...\n");
204        }
205#else
206        /* Obtain a valid lock on the mbus configuration file. This function */
207        /* creates the file, if one does not exist. The default contents of  */
208        /* this file are random authentication and encryption keys, and node */
209        /* local scope.                                                      */
210        struct flock     l;
211        struct stat      s;
212        struct passwd   *p;     
213        char            *buf;
214        char            *cfg_file;
215
216        /* The getpwuid() stuff is to determine the users home directory, into which we */
217        /* write a .mbus config file. The struct returned by getpwuid() is statically   */
218        /* allocated, so it's not necessary to free it afterwards.                      */
219        p = getpwuid(getuid());
220        if (p == NULL) {
221                perror("Unable to get passwd entry");
222                abort();
223        }
224        cfg_file = (char *) xmalloc(strlen(p->pw_dir) + 6);
225        sprintf(cfg_file, "%s/.mbus", p->pw_dir);
226        m->cfgfd = open(cfg_file, O_RDWR | O_CREAT, 0600);
227        if (m->cfgfd == -1) {
228                perror("Unable to open mbus configuration file");
229                abort();
230        }
231        xfree(cfg_file);
232
233        /* We attempt to get a lock on the config file, blocking until  */
234        /* the lock can be obtained. The only time this should block is */
235        /* when another instance of this code has a write lock on the   */
236        /* file, because the contents are being updated.                */
237        l.l_type   = F_WRLCK;
238        l.l_start  = 0;
239        l.l_whence = SEEK_SET;
240        l.l_len    = 0;
241        if (fcntl(m->cfgfd, F_SETLKW, &l) == -1) {
242                perror("Unable to lock mbus configuration file");
243                abort();
244        }
245
246        if (fstat(m->cfgfd, &s) != 0) {
247                perror("Unable to stat config file\n");
248                abort();
249        }
250        if (s.st_size == 0) {
251                /* Empty file, create with sensible defaults... */
252                char    *hashkey = mbus_new_hashkey();
253                char    *encrkey = mbus_new_encrkey();
254                char    *scope   = "HOSTLOCAL";
255                int      len;
256
257                len = strlen(hashkey) + strlen(encrkey) + strlen(scope) + 39;
258                buf = (char *) xmalloc(len);
259                sprintf(buf, "[MBUS]\nHASHKEY=%s\nENCRYPTIONKEY=%s\nSCOPE=%s\n", hashkey, encrkey, scope);
260                write(m->cfgfd, buf, strlen(buf));
261                xfree(buf);
262                free(hashkey);
263                xfree(encrkey);
264                debug_msg("Wrote config file\n");
265        } else {
266                /* Read in the contents of the config file... */
267                buf = (char *) xmalloc(s.st_size+1);
268                memset(buf, '\0', s.st_size+1);
269                if (read(m->cfgfd, buf, s.st_size) != s.st_size) {
270                        perror("Unable to read config file\n");
271                        abort();
272                }
273                /* Check that the file contains sensible information...   */
274                /* This is rather a pathetic check, but it'll do for now! */
275                if (strncmp(buf, "[MBUS]", 6) != 0) {
276                        debug_msg("Mbus config file has incorrect header\n");
277                        abort();
278                }
279                xfree(buf);
280        }
281#endif
282        m->cfg_locked = TRUE;
283}
284
285static void mbus_unlock_config_file(struct mbus *m)
286{
287#ifdef WIN32
288        LONG status;
289        char buffer[MBUS_BUF_SIZE];
290       
291        status = RegCloseKey(m->cfgKey);
292        if (status != ERROR_SUCCESS) {
293                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, MBUS_BUF_SIZE, NULL);
294                debug_msg("Unable to close registry: %s\n", buffer);
295                abort();
296        }
297        debug_msg("Closed registry entry...\n");
298#else
299        struct flock    l;
300
301        l.l_type   = F_UNLCK;
302        l.l_start  = 0;
303        l.l_whence = SEEK_SET;
304        l.l_len    = 0;
305        if (fcntl(m->cfgfd, F_SETLKW, &l) == -1) {
306                perror("Unable to unlock mbus configuration file");
307                abort();
308        }
309        close(m->cfgfd);
310        m->cfgfd = -1;
311#endif
312        m->cfg_locked = FALSE;
313}
314
315#ifndef WIN32
316static void mbus_get_key(struct mbus *m, struct mbus_key *key, char *id)
317{
318        struct stat      s;
319        char            *buf;
320        char            *line;
321        char            *tmp;
322        int              pos;
323
324        assert(m->cfg_locked);
325
326        if (lseek(m->cfgfd, 0, SEEK_SET) == -1) {
327                perror("Can't seek to start of config file");
328                abort();
329        }
330        if (fstat(m->cfgfd, &s) != 0) {
331                perror("Unable to stat config file\n");
332                abort();
333        }
334        /* Read in the contents of the config file... */
335        buf = (char *) xmalloc(s.st_size+1);
336        memset(buf, '\0', s.st_size+1);
337        if (read(m->cfgfd, buf, s.st_size) != s.st_size) {
338                perror("Unable to read config file\n");
339                abort();
340        }
341       
342        line = (char *) xmalloc(s.st_size+1);
343        sscanf(buf, "%s", line);
344        if (strcmp(line, "[MBUS]") != 0) {
345                debug_msg("Invalid .mbus file\n");
346                abort();
347        }
348        pos = strlen(line) + 1;
349        while (pos < s.st_size) {
350                sscanf(buf+pos, "%s", line);
351                pos += strlen(line) + 1;
352                if (strncmp(line, id, strlen(id)) == 0) {
353                        key->algorithm   = (char *) strdup(strtok(line+strlen(id), ",)"));
354                        if (strcmp(key->algorithm, "NOENCR") != 0) {
355                                key->key     = (char *) strtok(NULL  , ")");
356                                key->key_len = strlen(key->key);
357                                tmp = (char *) xmalloc(key->key_len);
358                                key->key_len = base64decode(key->key, key->key_len, tmp, key->key_len);
359                                key->key = tmp;
360                        } else {
361                                key->key     = NULL;
362                                key->key_len = 0;
363                        }
364                        xfree(buf);
365                        xfree(line);
366                        return;
367                }
368        }
369        debug_msg("Unable to read hashkey from config file\n");
370        xfree(buf);
371        xfree(line);
372}
373#endif
374
375static void mbus_get_encrkey(struct mbus *m, struct mbus_key *key)
376{
377        /* This MUST be called while the config file is locked! */
378        unsigned char   *des_key;
379        int              i, j, k;
380#ifdef WIN32
381        long             status;
382        DWORD            type;
383        char            *buffer;
384        int              buflen = MBUS_BUF_SIZE;
385        char            *tmp;
386
387        assert(m->cfg_locked);
388       
389        /* Read the key from the registry... */
390        buffer = (char *) xmalloc(MBUS_BUF_SIZE);
391        status = RegQueryValueEx(m->cfgKey, "ENCRYPTIONKEY", 0, &type, buffer, &buflen);
392        if (status != ERROR_SUCCESS) {
393                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, MBUS_BUF_SIZE, NULL);
394                debug_msg("Unable to get encrkey: %s\n", buffer);
395                abort();
396        }
397        assert(type == REG_SZ);
398        assert(buflen > 0);
399
400        /* Parse the key... */
401        key->algorithm   = strdup(strtok(buffer+1, ",)"));
402        if (strcmp(key->algorithm, "NOENCR") != 0) {
403                key->key     = (char *) strtok(NULL  , ")");
404                key->key_len = strlen(key->key);
405                tmp = (char *) xmalloc(key->key_len);
406                key->key_len = base64decode(key->key, key->key_len, tmp, key->key_len);
407                key->key = tmp;
408        } else {
409                key->key     = NULL;
410                key->key_len = 0;
411        }
412
413        debug_msg("alg=%s key=%s keylen=%d\n", key->algorithm, key->key, key->key_len);
414
415        xfree(buffer);
416#else
417        mbus_get_key(m, key, "ENCRYPTIONKEY=(");
418#endif
419        if (strcmp(key->algorithm, "DES") == 0) {
420                assert(key->key != NULL);
421                assert(key->key_len == 7);
422                /* Take the first 56-bits of the input key and spread it across   */
423                /* the 64-bit DES key space inserting a bit-space of garbage      */
424                /* (for parity) every 7 bits. The garbage will be taken care of   */
425                /* below. The library we're using expects the key and parity bits */
426                /* in the following MSB order: K0 K1...K6 P0 K8 K9...K14 P1...    */
427                des_key = (unsigned char *) xmalloc(8);
428                des_key[0] = key->key[0];
429                des_key[1] = key->key[0] << 7 | key->key[1] >> 1;
430                des_key[2] = key->key[1] << 6 | key->key[2] >> 2;
431                des_key[3] = key->key[2] << 5 | key->key[3] >> 3;
432                des_key[4] = key->key[3] << 4 | key->key[4] >> 4;
433                des_key[5] = key->key[4] << 3 | key->key[5] >> 5;
434                des_key[6] = key->key[5] << 2 | key->key[6] >> 6;
435                des_key[7] = key->key[6] << 1;
436
437                /* fill in parity bits to make DES library happy */
438                for (i = 0; i < 8; ++i)
439                {
440                        k = des_key[i] & 0xfe;
441                        j = k;
442                        j ^= j >> 4;
443                        j ^= j >> 2;
444                        j ^= j >> 1;
445                        j = (j & 1) ^ 1;
446                        des_key[i] = k | j;
447                }
448                xfree(key->key);
449                key->key     = des_key;
450                key->key_len = 8;
451        }
452}
453
454static void mbus_get_hashkey(struct mbus *m, struct mbus_key *key)
455{
456        /* This MUST be called while the config file is locked! */
457#ifdef WIN32
458        long     status;
459        DWORD    type;
460        char    *buffer;
461        int      buflen = MBUS_BUF_SIZE;
462        char    *tmp;
463
464        assert(m->cfg_locked);
465       
466        /* Read the key from the registry... */
467        buffer = (char *) xmalloc(MBUS_BUF_SIZE);
468        status = RegQueryValueEx(m->cfgKey, "HASHKEY", 0, &type, buffer, &buflen);
469        if (status != ERROR_SUCCESS) {
470                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, 0, buffer, MBUS_BUF_SIZE, NULL);
471                debug_msg("Unable to get encrkey: %s\n", buffer);
472                abort();
473        }
474        assert(type == REG_SZ);
475        assert(buflen > 0);
476
477        /* Parse the key... */
478        key->algorithm   = strdup(strtok(buffer+1, ","));
479        key->key         = strtok(NULL  , ")");
480        key->key_len     = strlen(key->key);
481
482        debug_msg("alg=%s key=%s keylen=%d\n", key->algorithm, key->key, key->key_len);
483
484        /* Decode the key... */
485        tmp = (char *) xmalloc(key->key_len);
486        key->key_len = base64decode(key->key, key->key_len, tmp, key->key_len);
487        key->key = tmp;
488
489        xfree(buffer);
490#else
491        mbus_get_key(m, key, "HASHKEY=(");
492#endif
493}
494
495static int mbus_addr_match(char *a, char *b)
496{
497        /* Compare the addresses "a" and "b". These may optionally be */
498        /* surrounded by "(" and ")" and may have an arbitrary amount */
499        /* of white space between components of the addresses. There  */
500        /* is a match if every word of address b is in address a.     */
501        /* NOTE: The strings passed to this function are stored for   */
502        /* later use and MUST NOT be modified by this routine.        */
503        char    *y, c;
504
505        assert(a != NULL);
506        assert(b != NULL);
507
508        /* Skip leading whitespace and '('... */
509        while (isspace((unsigned char)*a) || (*a == '(')) a++;
510        while (isspace((unsigned char)*b) || (*b == '(')) b++;
511
512        while ((*b != '\0') && (*b != ')')) {
513                while (isspace((unsigned char)*b)) b++;
514                for (y = b; ((*y != ' ') && (*y != ')') && (*y != '\0')); y++) {
515                        /* do nothing */
516                }
517                c = *y;
518                *y = '\0';
519                if (strstr(a, b) == NULL) {
520                        /* ...this word not found */
521                        *y = c;
522                        return FALSE;
523                }
524                *y = c;
525                b = y;
526        }               
527        return TRUE;
528}
529
530static void store_other_addr(struct mbus *m, char *a)
531{
532        /* This takes the address a and ensures it is stored in the   */
533        /* m->other_addr field of the mbus structure. The other_addr  */
534        /* field should probably be a hash table, but for now we hope */
535        /* that there are not too many entities on the mbus, so the   */
536        /* list is small.                                             */
537        int     i;
538
539        for (i = 0; i < m->num_other_addr; i++) {
540                if (mbus_addr_match(m->other_addr[i], a)) {
541                        /* Already in the list... */
542                        return;
543                }
544        }
545
546        if (m->num_other_addr == m->max_other_addr) {
547                /* Expand the list... */
548                m->max_other_addr *= 2;
549                m->other_addr = (char **) xrealloc(m->other_addr, m->max_other_addr * sizeof(char *));
550        }
551        m->other_addr[m->num_other_addr++] = xstrdup(a);
552}
553
554static int addr_known(struct mbus *m, char *a)
555{
556        int     i;
557
558        for (i = 0; i < m->num_other_addr; i++) {
559                if (mbus_addr_match(m->other_addr[i], a)) {
560                        return TRUE;
561                }
562        }
563        return FALSE;
564}
565
566/* The tx_* functions are used to build an mbus message up in the */
567/* tx_buffer, and to add authentication and encryption before the */
568/* message is sent.                                               */
569static char      tx_cryptbuf[MBUS_BUF_SIZE];
570static char      tx_buffer[MBUS_BUF_SIZE];
571static char     *tx_bufpos;
572
573#define MBUS_AUTH_LEN 25
574
575static void tx_header(int seqnum, int ts, char reliable, char *src, char *dst, int ackseq)
576{
577        memset(tx_buffer,   0, MBUS_BUF_SIZE);
578        memset(tx_buffer, ' ', MBUS_AUTH_LEN);
579        tx_bufpos = tx_buffer + MBUS_AUTH_LEN;
580        sprintf(tx_bufpos, "\nmbus/1.0 %6d %9d %c (%s) %s ", seqnum, ts, reliable, src, dst);
581        tx_bufpos += 33 + strlen(src) + strlen(dst);
582        if (ackseq == -1) {
583                sprintf(tx_bufpos, "()\n");
584                tx_bufpos += 3;
585        } else {
586                sprintf(tx_bufpos, "(%6d)\n", ackseq);
587                tx_bufpos += 9;
588        }
589}
590
591static void tx_add_command(char *cmnd, char *args)
592{
593        sprintf(tx_bufpos, "%s (%s)\n", cmnd, args);
594        tx_bufpos += strlen(cmnd) + strlen(args) + 4;
595}
596
597static void tx_send(struct mbus *m)
598{
599        char            digest[16];
600        int             len;
601        unsigned char   initVec[8] = {0,0,0,0,0,0,0,0};
602
603        while (((tx_bufpos - tx_buffer) % 8) != 0) {
604                /* Pad to a multiple of 8 bytes, so the encryption can work... */
605                *(tx_bufpos++) = ' ';
606        }
607        *tx_bufpos = '\0';
608        len = tx_bufpos - tx_buffer;
609
610        if (m->hashkey != NULL) {
611                /* Authenticate... */
612                hmac_md5(tx_buffer + MBUS_AUTH_LEN, strlen(tx_buffer) - MBUS_AUTH_LEN, m->hashkey, m->hashkeylen, digest);
613                base64encode(digest, 16, tx_buffer, MBUS_AUTH_LEN - 1);
614        }
615        if (m->encrkey != NULL) {
616                /* Encrypt... */
617                memset(tx_cryptbuf, 0, MBUS_BUF_SIZE);
618                memcpy(tx_cryptbuf, tx_buffer, len);
619                assert((len % 8) == 0);
620                assert(len < MBUS_BUF_SIZE);
621                assert(m->encrkeylen == 8);
622                qfDES_CBC_e(m->encrkey, tx_cryptbuf, len, initVec);
623                memcpy(tx_buffer, tx_cryptbuf, len);
624        }
625        udp_send(m->s, tx_buffer, len);
626}
627
628static void resend(struct mbus *m, struct mbus_msg *curr)
629{
630        /* Don't need to check for buffer overflows: this was done in mbus_send() when */
631        /* this message was first transmitted. If it was okay then, it's okay now.     */
632        int      i;
633
634        tx_header(curr->seqnum, curr->ts.tv_sec, (char)(curr->reliable?'R':'U'), m->addr[0], curr->dest, -1);
635        for (i = 0; i < curr->num_cmds; i++) {
636                tx_add_command(curr->cmd_list[i], curr->arg_list[i]);
637        }
638        tx_send(m);
639        curr->retransmit_count++;
640}
641
642void mbus_retransmit(struct mbus *m)
643{
644        struct mbus_msg *curr = m->waiting_ack;
645        struct timeval  time;
646        long            diff;
647
648        if (!mbus_waiting_ack(m)) {
649                return;
650        }
651
652        gettimeofday(&time, NULL);
653
654        /* diff is time in milliseconds that the message has been awaiting an ACK */
655        diff = ((time.tv_sec * 1000) + (time.tv_usec / 1000)) - ((curr->time.tv_sec * 1000) + (curr->time.tv_usec / 1000));
656        if (diff > 10000) {
657                debug_msg("Reliable mbus message failed!\n");
658                if (m->err_handler == NULL) {
659                        abort();
660                }
661                m->err_handler(curr->seqnum, MBUS_MESSAGE_LOST);
662                return;
663        }
664        /* Note: We only send one retransmission each time, to avoid
665         * overflowing the receiver with a burst of requests...
666         */
667        if ((diff > 750) && (curr->retransmit_count == 2)) {
668                resend(m, curr);
669                return;
670        }
671        if ((diff > 500) && (curr->retransmit_count == 1)) {
672                resend(m, curr);
673                return;
674        }
675        if ((diff > 250) && (curr->retransmit_count == 0)) {
676                resend(m, curr);
677                return;
678        }
679        curr = curr->next;
680}
681
682void mbus_heartbeat(struct mbus *m, int interval)
683{
684        struct timeval  curr_time;
685
686        gettimeofday(&curr_time, NULL);
687
688        if (curr_time.tv_sec - m->last_heartbeat.tv_sec > interval) {
689                mbus_qmsg(m, "()", "mbus.hello", "", FALSE);
690                m->last_heartbeat = curr_time;
691        }
692}
693
694int mbus_waiting_ack(struct mbus *m)
695{
696        return m->waiting_ack != NULL;
697}
698
699struct mbus *mbus_init(void  (*cmd_handler)(char *src, char *cmd, char *arg, void *dat),
700                       void  (*err_handler)(int seqnum, int reason))
701{
702        struct mbus     *m;
703        struct mbus_key  k;
704        int              i;
705
706        m = (struct mbus *) xmalloc(sizeof(struct mbus));
707        if (m == NULL) {
708                debug_msg("Unable to allocate memory for mbus\n");
709                return NULL;
710        }
711
712        mbus_lock_config_file(m);
713        m->s              = udp_init("224.255.222.239", (u_int16) 47000, 0);
714        m->seqnum         = 0;
715        m->cmd_handler    = cmd_handler;
716        m->err_handler    = err_handler;
717        m->num_addr       = 0;
718        m->num_other_addr = 0;
719        m->max_other_addr = 10;
720        m->other_addr     = (char **) xmalloc(sizeof(char *) * 10);
721        m->parse_depth    = 0;
722        m->cmd_queue      = NULL;
723        m->waiting_ack    = NULL;
724
725        gettimeofday(&(m->last_heartbeat), NULL);
726
727        mbus_get_encrkey(m, &k);
728        m->encrkey    = k.key;
729        m->encrkeylen = k.key_len;
730
731        mbus_get_hashkey(m, &k);
732        m->hashkey    = k.key;
733        m->hashkeylen = k.key_len;
734
735        for (i = 0; i < MBUS_MAX_ADDR; i++) m->addr[i]         = NULL;
736        for (i = 0; i < MBUS_MAX_PD;   i++) m->parse_buffer[i] = NULL;
737        mbus_unlock_config_file(m);
738
739        return m;
740}
741
742static void mbus_flush_msgs(struct mbus_msg *queue)
743{
744        struct mbus_msg *curr, *next;
745        int i;
746
747        curr = queue;
748        while(curr) {
749                next = curr->next;
750                xfree(curr->dest);
751                for(i = 0; i < curr->num_cmds; i++) {
752                        xfree(curr->cmd_list[i]);
753                        xfree(curr->arg_list[i]);
754                }
755                curr = next;
756        }
757}
758
759void mbus_exit(struct mbus *m)
760{
761        assert(m != NULL);
762
763        while(m->parse_depth--) {
764                xfree(m->parse_buffer[m->parse_depth]);
765        }
766
767        mbus_flush_msgs(m->cmd_queue);
768        mbus_flush_msgs(m->waiting_ack);
769
770        if (m->encrkey != NULL) {
771                xfree(m->encrkey);
772        }
773        xfree(m->hashkey);
774        xfree(m);
775}
776
777void mbus_addr(struct mbus *m, char *addr)
778{
779        assert(m->num_addr < MBUS_MAX_ADDR);
780        mbus_parse_init(m, xstrdup(addr));
781        if (mbus_parse_lst(m, &(m->addr[m->num_addr]))) {
782                m->num_addr++;
783        }
784        mbus_parse_done(m);
785}
786
787void mbus_send(struct mbus *m)
788{
789        /* Send one, or more, messages previosly queued with mbus_qmsg(). */
790        /* Messages for the same destination are batched together. Stops  */
791        /* when a reliable message is sent, until the ACK is received.    */
792        struct mbus_msg *curr = m->cmd_queue;
793        int              i;
794
795        if (m->waiting_ack != NULL) {
796                return;
797        }
798
799        while (curr != NULL) {
800                /* Create the message... */
801                tx_header(curr->seqnum, curr->ts.tv_sec, (char)(curr->reliable?'R':'U'), m->addr[0], curr->dest, -1);
802                for (i = 0; i < curr->num_cmds; i++) {
803                        tx_add_command(curr->cmd_list[i], curr->arg_list[i]);
804                }
805                tx_send(m);
806               
807                m->cmd_queue = curr->next;
808                if (curr->reliable) {
809                        /* Reliable message, wait for the ack... */
810                        gettimeofday(&(curr->time), NULL);
811                        m->waiting_ack = curr;
812                        return;
813                } else {
814                        while (curr->num_cmds > 0) {
815                                curr->num_cmds--;
816                                xfree(curr->cmd_list[curr->num_cmds]);
817                                xfree(curr->arg_list[curr->num_cmds]);
818                        }
819                        xfree(curr->dest);
820                        xfree(curr);
821                }
822                curr = m->cmd_queue;
823        }
824}
825
826void mbus_qmsg(struct mbus *m, char *dest, const char *cmnd, const char *args, int reliable)
827{
828        /* Queue up a message for sending. The message is not */
829        /* actually sent until mbus_send() is called.         */
830        struct mbus_msg *curr = m->cmd_queue;
831        struct mbus_msg *prev = NULL;
832        int              alen = strlen(cmnd) + strlen(args) + 4;
833
834
835        if (reliable && !addr_known(m, dest)) {
836                debug_msg("Trying to send reliably to an unknown address...\n");
837#ifdef NDEF
838                if (m->err_handler == NULL) {
839                        abort();
840                }
841                m->err_handler(curr->seqnum, MBUS_DESTINATION_UNKNOWN);
842#endif
843        }
844
845
846        while (curr != NULL) {
847                if (mbus_addr_match(curr->dest, dest)
848                && (curr->num_cmds < MBUS_MAX_QLEN)
849                && ((curr->message_size + alen) < (MBUS_BUF_SIZE - 8))) {
850                        curr->num_cmds++;
851                        curr->reliable |= reliable;
852                        curr->cmd_list[curr->num_cmds-1] = xstrdup(cmnd);
853                        curr->arg_list[curr->num_cmds-1] = xstrdup(args);
854                        curr->message_size += alen;
855                        return;
856                }
857                prev = curr;
858                curr = curr->next;
859        }
860        curr = (struct mbus_msg *) xmalloc(sizeof(struct mbus_msg));
861        curr->next             = NULL;
862        curr->dest             = xstrdup(dest);
863        curr->retransmit_count = 0;
864        curr->message_size     = alen + 60 + strlen(dest) + strlen(m->addr[0]);
865        curr->seqnum           = m->seqnum++;
866        curr->reliable         = reliable;
867        curr->num_cmds         = 1;
868        curr->cmd_list[0]      = xstrdup(cmnd);
869        curr->arg_list[0]      = xstrdup(args);
870        if (prev == NULL) {
871                m->cmd_queue = curr;
872        } else {
873                prev->next = curr;
874        }
875        gettimeofday(&(curr->time), NULL);
876        gettimeofday(&(curr->ts),   NULL);
877}
878
879void mbus_qmsgf(struct mbus *m, char *dest, int reliable, const char *cmnd, const char *format, ...)
880{
881        /* This is a wrapper around mbus_qmsg() which does a printf() style format into  */
882        /* a buffer. Saves the caller from having to a a malloc(), write the args string */
883        /* and then do a free(), and also saves worring about overflowing the buffer, so */
884        /* removing a common source of bugs!                                             */
885        char    buffer[MBUS_BUF_SIZE];
886        va_list ap;
887
888        va_start(ap, format);
889#ifdef WIN32
890        _vsnprintf(buffer, MBUS_BUF_SIZE, format, ap);
891#else
892        vsnprintf(buffer, MBUS_BUF_SIZE, format, ap);
893#endif
894        va_end(ap);
895        mbus_qmsg(m, dest, cmnd, buffer, reliable);
896}
897
898void mbus_parse_init(struct mbus *m, char *str)
899{
900        assert(m->parse_depth < (MBUS_MAX_PD - 1));
901        m->parse_buffer[++m->parse_depth] = str;
902}
903
904void mbus_parse_done(struct mbus *m)
905{
906        m->parse_depth--;
907        assert(m->parse_depth >= 0);
908}
909
910int mbus_parse_lst(struct mbus *m, char **l)
911{
912        int instr = FALSE;
913        int inlst = FALSE;
914
915        *l = m->parse_buffer[m->parse_depth];
916        while (isspace((unsigned char)*m->parse_buffer[m->parse_depth])) {
917                m->parse_buffer[m->parse_depth]++;
918        }
919        if (*m->parse_buffer[m->parse_depth] != '(') {
920                return FALSE;
921        }
922        *(m->parse_buffer[m->parse_depth]) = ' ';
923        while (*m->parse_buffer[m->parse_depth] != '\0') {
924                if ((*m->parse_buffer[m->parse_depth] == '"') && (*(m->parse_buffer[m->parse_depth]-1) != '\\')) {
925                        instr = !instr;
926                }
927                if ((*m->parse_buffer[m->parse_depth] == '(') && (*(m->parse_buffer[m->parse_depth]-1) != '\\') && !instr) {
928                        inlst = !inlst;
929                }
930                if ((*m->parse_buffer[m->parse_depth] == ')') && (*(m->parse_buffer[m->parse_depth]-1) != '\\') && !instr) {
931                        if (inlst) {
932                                inlst = !inlst;
933                        } else {
934                                *m->parse_buffer[m->parse_depth] = '\0';
935                                m->parse_buffer[m->parse_depth]++;
936                                return TRUE;
937                        }
938                }
939                m->parse_buffer[m->parse_depth]++;
940        }
941        return FALSE;
942}
943
944int mbus_parse_str(struct mbus *m, char **s)
945{
946        while (isspace((unsigned char)*m->parse_buffer[m->parse_depth])) {
947                m->parse_buffer[m->parse_depth]++;
948        }
949        if (*m->parse_buffer[m->parse_depth] != '"') {
950                return FALSE;
951        }
952        *s = m->parse_buffer[m->parse_depth]++;
953        while (*m->parse_buffer[m->parse_depth] != '\0') {
954                if ((*m->parse_buffer[m->parse_depth] == '"') && (*(m->parse_buffer[m->parse_depth]-1) != '\\')) {
955                        m->parse_buffer[m->parse_depth]++;
956                        *m->parse_buffer[m->parse_depth] = '\0';
957                        m->parse_buffer[m->parse_depth]++;
958                        return TRUE;
959                }
960                m->parse_buffer[m->parse_depth]++;
961        }
962        return FALSE;
963}
964
965static int mbus_parse_sym(struct mbus *m, char **s)
966{
967        while (isspace((unsigned char)*m->parse_buffer[m->parse_depth])) {
968                m->parse_buffer[m->parse_depth]++;
969        }
970        if (!isgraph((unsigned char)*m->parse_buffer[m->parse_depth])) {
971                return FALSE;
972        }
973        *s = m->parse_buffer[m->parse_depth]++;
974        while (!isspace((unsigned char)*m->parse_buffer[m->parse_depth]) && (*m->parse_buffer[m->parse_depth] != '\0')) {
975                m->parse_buffer[m->parse_depth]++;
976        }
977        *m->parse_buffer[m->parse_depth] = '\0';
978        m->parse_buffer[m->parse_depth]++;
979        return TRUE;
980}
981
982int mbus_parse_int(struct mbus *m, int *i)
983{
984        char    *p;
985
986        while (isspace((unsigned char)*m->parse_buffer[m->parse_depth])) {
987                m->parse_buffer[m->parse_depth]++;
988        }
989
990        *i = strtol(m->parse_buffer[m->parse_depth], &p, 10);
991        if (((*i == LONG_MAX) || (*i == LONG_MIN)) && (errno == ERANGE)) {
992                debug_msg("integer out of range\n");
993                return FALSE;
994        }
995
996        if (p == m->parse_buffer[m->parse_depth]) {
997                return FALSE;
998        }
999        if (!isspace((unsigned char)*p) && (*p != '\0')) {
1000                return FALSE;
1001        }
1002        m->parse_buffer[m->parse_depth] = p;
1003        return TRUE;
1004}
1005
1006int mbus_parse_flt(struct mbus *m, double *d)
1007{
1008        char    *p;
1009        while (isspace((unsigned char)*m->parse_buffer[m->parse_depth])) {
1010                m->parse_buffer[m->parse_depth]++;
1011        }
1012
1013        *d = strtod(m->parse_buffer[m->parse_depth], &p);
1014        if (errno == ERANGE) {
1015                debug_msg("float out of range\n");
1016                return FALSE;
1017        }
1018
1019        if (p == m->parse_buffer[m->parse_depth]) {
1020                return FALSE;
1021        }
1022        if (!isspace((unsigned char)*p) && (*p != '\0')) {
1023                return FALSE;
1024        }
1025        m->parse_buffer[m->parse_depth] = p;
1026        return TRUE;
1027}
1028
1029char *mbus_decode_str(char *s)
1030{
1031        int     l = strlen(s);
1032        int     i, j;
1033
1034        /* Check that this an encoded string... */
1035        assert(s[0]   == '\"');
1036        assert(s[l-1] == '\"');
1037
1038        for (i=1,j=0; i < l - 1; i++,j++) {
1039                if (s[i] == '\\') {
1040                        i++;
1041                }
1042                s[j] = s[i];
1043        }
1044        s[j] = '\0';
1045        return s;
1046}
1047
1048char *mbus_encode_str(const char *s)
1049{
1050        int      i, j;
1051        int      len = strlen(s);
1052        char    *buf = (char *) xmalloc((len * 2) + 3);
1053
1054        for (i = 0, j = 1; i < len; i++,j++) {
1055                if (s[i] == ' ') {
1056                        buf[j] = '\\';
1057                        buf[j+1] = ' ';
1058                        j++;
1059                } else if (s[i] == '\"') {
1060                        buf[j] = '\\';
1061                        buf[j+1] = '\"';
1062                        j++;
1063                } else {
1064                        buf[j] = s[i];
1065                }
1066        }
1067        buf[0]   = '\"';
1068        buf[j]   = '\"';
1069        buf[j+1] = '\0';
1070        return buf;
1071}
1072
1073int mbus_recv(struct mbus *m, void *data)
1074{
1075        char            *auth, *ver, *src, *dst, *ack, *r, *cmd, *param;
1076        char            buffer[MBUS_BUF_SIZE];
1077        int             buffer_len, seq, i, a, rx, ts, authlen;
1078        char            ackbuf[MBUS_ACK_BUF_SIZE];
1079        char            digest[16];
1080        struct timeval  t;
1081        unsigned char   initVec[8] = {0,0,0,0,0,0,0,0};
1082
1083        rx = FALSE;
1084        while (1) {
1085                memset(buffer, 0, MBUS_BUF_SIZE);
1086                assert(m->s != NULL);
1087                udp_fd_zero();
1088                udp_fd_set(m->s);
1089                t.tv_sec  = 0; /* timeout appears to get corrupted on w32 */
1090                t.tv_usec = 0; /* sometimes, reset to zero everytime.     */ 
1091                if ((udp_select(&t) > 0) && udp_fd_isset(m->s)) {
1092                        buffer_len = udp_recv(m->s, buffer, MBUS_BUF_SIZE);
1093                        if (buffer_len > 0) {
1094                                rx = TRUE;
1095                        } else {
1096                                return rx;
1097                        }
1098                } else {
1099                        return FALSE;
1100                }
1101
1102                if (m->encrkey != NULL) {
1103                        /* Decrypt the message... */
1104                        if ((buffer_len % 8) != 0) {
1105                                debug_msg("Encrypted message not a multiple of 8 bytes in length\n");
1106                                continue;
1107                        }
1108                        memcpy(tx_cryptbuf, buffer, buffer_len);
1109                        memset(initVec, 0, 8);
1110                        qfDES_CBC_d(m->encrkey, tx_cryptbuf, buffer_len, initVec);
1111                        if (strncmp(tx_cryptbuf + MBUS_AUTH_LEN + 1, "mbus/1.0", 8) != 0) {
1112                                debug_msg("Message did not correctly decrypt\n");
1113                                continue;
1114                        }
1115                        memcpy(buffer, tx_cryptbuf, buffer_len);
1116                }
1117
1118                mbus_parse_init(m, buffer);
1119                /* Parse the authentication header */
1120                if (!mbus_parse_sym(m, &auth)) {
1121                        debug_msg("Failed to parse authentication header\n");
1122                        mbus_parse_done(m);
1123                        continue;
1124                }
1125
1126                /* Check that the packet authenticates correctly... */
1127                authlen = strlen(auth);
1128                hmac_md5(buffer + authlen + 1, buffer_len - authlen - 1, m->hashkey, m->hashkeylen, digest);
1129                base64encode(digest, 16, ackbuf, 24);
1130                if ((strlen(auth) != 24) || (strncmp(auth, ackbuf, 24) != 0)) {
1131                        debug_msg("Failed to authenticate message...\n");
1132                        mbus_parse_done(m);
1133                        continue;
1134                }
1135
1136                /* Parse the header */
1137                if (!mbus_parse_sym(m, &ver)) {
1138                        mbus_parse_done(m);
1139                        debug_msg("Parser failed version (1): %s\n",ver);
1140                        continue;
1141                }
1142                if (strcmp(ver, "mbus/1.0") != 0) {
1143                        mbus_parse_done(m);
1144                        debug_msg("Parser failed version (2): %s\n",ver);
1145                        continue;
1146                }
1147                if (!mbus_parse_int(m, &seq)) {
1148                        mbus_parse_done(m);
1149                        debug_msg("Parser failed seq\n");
1150                        continue;
1151                }
1152                if (!mbus_parse_int(m, &ts)) {
1153                        mbus_parse_done(m);
1154                        debug_msg("Parser failed ts\n");
1155                        continue;
1156                }
1157                if (!mbus_parse_sym(m, &r)) {
1158                        mbus_parse_done(m);
1159                        debug_msg("Parser failed reliable\n");
1160                        continue;
1161                }
1162                if (!mbus_parse_lst(m, &src)) {
1163                        mbus_parse_done(m);
1164                        debug_msg("Parser failed src\n");
1165                        continue;
1166                }
1167                if (!mbus_parse_lst(m, &dst)) {
1168                        mbus_parse_done(m);
1169                        debug_msg("Parser failed dst\n");
1170                        continue;
1171                }
1172                if (!mbus_parse_lst(m, &ack)) {
1173                        mbus_parse_done(m);
1174                        debug_msg("Parser failed ack\n");
1175                        continue;
1176                }
1177
1178                store_other_addr(m, src);
1179
1180                /* Check if the message was addressed to us... */
1181                for (i = 0; i < m->num_addr; i++) {
1182                        if (mbus_addr_match(m->addr[i], dst)) {
1183                                /* ...if so, process any ACKs received... */
1184                                mbus_parse_init(m, ack);
1185                                while (mbus_parse_int(m, &a)) {
1186                                        if (mbus_waiting_ack(m) && (m->waiting_ack->seqnum == a)) {
1187                                                while (m->waiting_ack->num_cmds > 0) {
1188                                                        m->waiting_ack->num_cmds--;
1189                                                        xfree(m->waiting_ack->cmd_list[m->waiting_ack->num_cmds]);
1190                                                        xfree(m->waiting_ack->arg_list[m->waiting_ack->num_cmds]);
1191                                                }
1192                                                xfree(m->waiting_ack->dest);
1193                                                xfree(m->waiting_ack);
1194                                                m->waiting_ack = NULL;
1195                                        }
1196                                }
1197                                mbus_parse_done(m);
1198                                /* ...if an ACK was requested, send one... */
1199                                if (strcmp(r, "R") == 0) {
1200                                        char *newsrc = (char *) xmalloc(strlen(src) + 3);
1201                                        sprintf(newsrc, "(%s)", src);   /* Yes, this is a kludge. */
1202                                        gettimeofday(&t, NULL);
1203                                        tx_header(++m->seqnum, (int) t.tv_sec, 'U', m->addr[0], newsrc, seq);
1204                                        tx_send(m);
1205                                        xfree(newsrc);
1206                                }
1207                                /* ...and process the commands contained in the message */
1208                                while (mbus_parse_sym(m, &cmd)) {
1209                                        if (mbus_parse_lst(m, &param)) {
1210                                                m->cmd_handler(src, cmd, param, data);
1211                                        } else {
1212                                                debug_msg("Unable to parse mbus command:\n");
1213                                                debug_msg("cmd = %s\n", cmd);
1214                                                debug_msg("arg = %s\n", param);
1215                                                break;
1216                                        }
1217                                }
1218                        }
1219                }
1220                mbus_parse_done(m);
1221        }
1222}
Note: See TracBrowser for help on using the browser.