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

Revision 143, 34.7 KB (checked in by ucaccsp, 15 years ago)

Fix reordering of mbus messages -- just in time for Ally McBeal?! :-)

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