alias_proxy.c revision 45008
1/* file: alias_proxy.c
2
3    This file encapsulates special operations related to transparent
4    proxy redirection.  This is where packets with a particular destination,
5    usually tcp port 80, are redirected to a proxy server.
6
7    When packets are proxied, the destination address and port are
8    modified.  In certain cases, it is necessary to somehow encode
9    the original address/port info into the packet.  Two methods are
10    presently supported: addition of a [DEST addr port] string at the
11    beginning a of tcp stream, or inclusion of an optional field
12    in the IP header.
13
14    There is one public API function:
15
16        PacketAliasProxyRule()    -- Adds and deletes proxy
17                                     rules.
18
19    Rules are stored in a linear linked list, so lookup efficiency
20    won't be too good for large lists.
21
22
23    Initial development: April, 1998 (cjm)
24*/
25
26
27/* System includes */
28#include <ctype.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <netdb.h>
33
34#include <sys/types.h>
35#include <sys/socket.h>
36
37/* BSD IPV4 includes */
38#include <netinet/in_systm.h>
39#include <netinet/in.h>
40#include <netinet/ip.h>
41#include <netinet/tcp.h>
42
43#include <arpa/inet.h>
44
45#include "alias_local.h"  /* Functions used by alias*.c */
46#include "alias.h"        /* Public API functions for libalias */
47
48
49
50/*
51    Data structures
52 */
53
54/*
55 * A linked list of arbitrary length, based on struct proxy_entry is
56 * used to store proxy rules.
57 */
58struct proxy_entry
59{
60#define PROXY_TYPE_ENCODE_NONE      1
61#define PROXY_TYPE_ENCODE_TCPSTREAM 2
62#define PROXY_TYPE_ENCODE_IPHDR     3
63    int rule_index;
64    int proxy_type;
65    u_char proto;
66    u_short proxy_port;
67    u_short server_port;
68
69    struct in_addr server_addr;
70
71    struct in_addr src_addr;
72    struct in_addr src_mask;
73
74    struct in_addr dst_addr;
75    struct in_addr dst_mask;
76
77    struct proxy_entry *next;
78    struct proxy_entry *last;
79};
80
81
82
83/*
84    File scope variables
85*/
86
87static struct proxy_entry *proxyList;
88
89
90
91/* Local (static) functions:
92
93    IpMask()                 -- Utility function for creating IP
94                                masks from integer (1-32) specification.
95    IpAddr()                 -- Utility function for converting string
96                                to IP address
97    IpPort()                 -- Utility function for converting string
98                                to port number
99    RuleAdd()                -- Adds an element to the rule list.
100    RuleDelete()             -- Removes an element from the rule list.
101    RuleNumberDelete()       -- Removes all elements from the rule list
102                                having a certain rule number.
103    ProxyEncodeTcpStream()   -- Adds [DEST x.x.x.x xxxx] to the beginning
104                                of a TCP stream.
105    ProxyEncodeIpHeader()    -- Adds an IP option indicating the true
106                                destination of a proxied IP packet
107*/
108
109static int IpMask(int, struct in_addr *);
110static int IpAddr(char *, struct in_addr *);
111static int IpPort(char *, int, int *);
112static void RuleAdd(struct proxy_entry *);
113static void RuleDelete(struct proxy_entry *);
114static int RuleNumberDelete(int);
115static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
116static void ProxyEncodeIpHeader(struct ip *, int);
117
118static int
119IpMask(int nbits, struct in_addr *mask)
120{
121    int i;
122    u_int imask;
123
124    if (nbits < 0 || nbits > 32)
125        return -1;
126
127    imask = 0;
128    for (i=0; i<nbits; i++)
129        imask = (imask >> 1) + 0x80000000;
130    mask->s_addr = htonl(imask);
131
132    return 0;
133}
134
135static int
136IpAddr(char *s, struct in_addr *addr)
137{
138    if (inet_aton(s, addr) == 0)
139        return -1;
140    else
141        return 0;
142}
143
144static int
145IpPort(char *s, int proto, int *port)
146{
147    int n;
148
149    n = sscanf(s, "%d", port);
150    if (n != 1)
151    {
152        struct servent *se;
153
154        if (proto == IPPROTO_TCP)
155            se = getservbyname(s, "tcp");
156        else if (proto == IPPROTO_UDP)
157            se = getservbyname(s, "udp");
158        else
159            return -1;
160
161        if (se == NULL)
162                return -1;
163
164        *port = (u_int) ntohs(se->s_port);
165    }
166
167    return 0;
168}
169
170void
171RuleAdd(struct proxy_entry *entry)
172{
173    int rule_index;
174    struct proxy_entry *ptr;
175    struct proxy_entry *ptr_last;
176
177    if (proxyList == NULL)
178    {
179        proxyList = entry;
180        entry->last = NULL;
181        entry->next = NULL;
182        return;
183    }
184
185    rule_index = entry->rule_index;
186    ptr = proxyList;
187    ptr_last = NULL;
188    while (ptr != NULL)
189    {
190        if (ptr->rule_index >= rule_index)
191        {
192            if (ptr_last == NULL)
193            {
194                entry->next = proxyList;
195                entry->last = NULL;
196                proxyList->last = entry;
197                proxyList = entry;
198                return;
199            }
200
201            ptr_last->next = entry;
202            ptr->last = entry;
203            entry->last = ptr->last;
204            entry->next = ptr;
205            return;
206        }
207        ptr_last = ptr;
208        ptr = ptr->next;
209    }
210
211    ptr_last->next = entry;
212    entry->last = ptr_last;
213    entry->next = NULL;
214}
215
216static void
217RuleDelete(struct proxy_entry *entry)
218{
219    if (entry->last != NULL)
220        entry->last->next = entry->next;
221    else
222        proxyList = entry->next;
223
224    if (entry->next != NULL)
225        entry->next->last = entry->last;
226
227    free(entry);
228}
229
230static int
231RuleNumberDelete(int rule_index)
232{
233    int err;
234    struct proxy_entry *ptr;
235
236    err = -1;
237    ptr = proxyList;
238    while (ptr != NULL)
239    {
240        struct proxy_entry *ptr_next;
241
242        ptr_next = ptr->next;
243        if (ptr->rule_index == rule_index)
244        {
245            err = 0;
246            RuleDelete(ptr);
247        }
248
249        ptr = ptr_next;
250    }
251
252    return err;
253}
254
255static void
256ProxyEncodeTcpStream(struct alias_link *link,
257                     struct ip *pip,
258                     int maxpacketsize)
259{
260    int slen;
261    char buffer[40];
262    struct tcphdr *tc;
263
264/* Compute pointer to tcp header */
265    tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
266
267/* Don't modify if once already modified */
268
269    if (GetAckModified (link))
270	return;
271
272/* Translate destination address and port to string form */
273    snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
274        inet_ntoa(GetProxyAddress (link)), (u_int) ntohs(GetProxyPort (link)));
275
276/* Pad string out to a multiple of two in length */
277    slen = strlen(buffer);
278    switch (slen % 2)
279    {
280    case 0:
281        strcat(buffer, " \n");
282	slen += 2;
283        break;
284    case 1:
285        strcat(buffer, "\n");
286	slen += 1;
287    }
288
289/* Check for packet overflow */
290    if ((ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
291        return;
292
293/* Shift existing TCP data and insert destination string */
294    {
295        int dlen;
296        int hlen;
297        u_char *p;
298
299        hlen = (pip->ip_hl + tc->th_off) << 2;
300        dlen = ntohs (pip->ip_len) - hlen;
301
302/* Modify first packet that has data in it */
303
304	if (dlen == 0)
305		return;
306
307        p = (char *) pip;
308        p += hlen;
309
310        memmove(p + slen, p, dlen);
311        memcpy(p, buffer, slen);
312    }
313
314/* Save information about modfied sequence number */
315    {
316        int delta;
317
318        SetAckModified(link);
319        delta = GetDeltaSeqOut(pip, link);
320        AddSeq(pip, link, delta+slen);
321    }
322
323/* Update IP header packet length and checksum */
324    {
325        int accumulate;
326
327        accumulate  = pip->ip_len;
328        pip->ip_len = htons(ntohs(pip->ip_len) + slen);
329        accumulate -= pip->ip_len;
330
331        ADJUST_CHECKSUM(accumulate, pip->ip_sum);
332    }
333
334/* Update TCP checksum, Use TcpChecksum since so many things have
335   already changed. */
336
337    tc->th_sum = 0;
338    tc->th_sum = TcpChecksum (pip);
339}
340
341static void
342ProxyEncodeIpHeader(struct ip *pip,
343                    int maxpacketsize)
344{
345#define OPTION_LEN_BYTES  8
346#define OPTION_LEN_INT16  4
347#define OPTION_LEN_INT32  2
348    u_char option[OPTION_LEN_BYTES];
349
350#ifdef DEBUG
351    fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
352    fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
353#endif
354
355/* Check to see that there is room to add an IP option */
356    if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
357        return;
358
359/* Build option and copy into packet */
360    {
361        u_char *ptr;
362        struct tcphdr *tc;
363
364        ptr = (u_char *) pip;
365        ptr += 20;
366        memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
367
368        option[0] = 0x64; /* class: 3 (reserved), option 4 */
369        option[1] = OPTION_LEN_BYTES;
370
371        memcpy(&option[2], (u_char *) &pip->ip_dst, 4);
372
373        tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
374        memcpy(&option[6], (u_char *) &tc->th_sport, 2);
375
376        memcpy(ptr, option, 8);
377    }
378
379/* Update checksum, header length and packet length */
380    {
381        int i;
382        int accumulate;
383        u_short *sptr;
384
385        sptr = (u_short *) option;
386        accumulate = 0;
387        for (i=0; i<OPTION_LEN_INT16; i++)
388            accumulate -= *(sptr++);
389
390        sptr = (u_short *) pip;
391        accumulate += *sptr;
392        pip->ip_hl += OPTION_LEN_INT32;
393        accumulate -= *sptr;
394
395        accumulate += pip->ip_len;
396        pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
397        accumulate -= pip->ip_len;
398
399        ADJUST_CHECKSUM(accumulate, pip->ip_sum);
400    }
401#undef OPTION_LEN_BYTES
402#undef OPTION_LEN_INT16
403#undef OPTION_LEN_INT32
404#ifdef DEBUG
405    fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
406    fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
407#endif
408}
409
410
411/* Functions by other packet alias source files
412
413    ProxyCheck()         -- Checks whether an outgoing packet should
414                            be proxied.
415    ProxyModify()        -- Encodes the original destination address/port
416                            for a packet which is to be redirected to
417                            a proxy server.
418*/
419
420int
421ProxyCheck(struct ip *pip,
422           struct in_addr *proxy_server_addr,
423           u_short *proxy_server_port)
424{
425    u_short dst_port;
426    struct in_addr src_addr;
427    struct in_addr dst_addr;
428    struct proxy_entry *ptr;
429
430    src_addr = pip->ip_src;
431    dst_addr = pip->ip_dst;
432    dst_port = ((struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)))
433        ->th_dport;
434
435    ptr = proxyList;
436    while (ptr != NULL)
437    {
438        u_short proxy_port;
439
440        proxy_port = ptr->proxy_port;
441        if ((dst_port == proxy_port || proxy_port == 0)
442         && pip->ip_p == ptr->proto
443         && src_addr.s_addr != ptr->server_addr.s_addr)
444        {
445            struct in_addr src_addr_masked;
446            struct in_addr dst_addr_masked;
447
448            src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
449            dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
450
451            if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
452             && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr))
453            {
454                if ((*proxy_server_port = ptr->server_port) == 0)
455                    *proxy_server_port = dst_port;
456                *proxy_server_addr = ptr->server_addr;
457                return ptr->proxy_type;
458            }
459        }
460        ptr = ptr->next;
461    }
462
463    return 0;
464}
465
466void
467ProxyModify(struct alias_link *link,
468            struct ip *pip,
469            int maxpacketsize,
470            int proxy_type)
471{
472    switch (proxy_type)
473    {
474    case PROXY_TYPE_ENCODE_IPHDR:
475        ProxyEncodeIpHeader(pip, maxpacketsize);
476        break;
477
478    case PROXY_TYPE_ENCODE_TCPSTREAM:
479        ProxyEncodeTcpStream(link, pip, maxpacketsize);
480        break;
481    }
482}
483
484
485/*
486    Public API functions
487*/
488
489int
490PacketAliasProxyRule(const char *cmd)
491{
492/*
493 * This function takes command strings of the form:
494 *
495 *   server <addr>[:<port>]
496 *   [port <port>]
497 *   [rule n]
498 *   [proto tcp|udp]
499 *   [src <addr>[/n]]
500 *   [dst <addr>[/n]]
501 *   [type encode_tcp_stream|encode_ip_hdr|no_encode]
502 *
503 *   delete <rule number>
504 *
505 * Subfields can be in arbitrary order.  Port numbers and addresses
506 * must be in either numeric or symbolic form. An optional rule number
507 * is used to control the order in which rules are searched.  If two
508 * rules have the same number, then search order cannot be guaranteed,
509 * and the rules should be disjoint.  If no rule number is specified,
510 * then 0 is used, and group 0 rules are always checked before any
511 * others.
512 */
513    int i, n, len;
514    int cmd_len;
515    int token_count;
516    int state;
517    char *token;
518    char buffer[256];
519    char str_port[sizeof(buffer)];
520    char str_server_port[sizeof(buffer)];
521
522    int rule_index;
523    int proto;
524    int proxy_type;
525    int proxy_port;
526    int server_port;
527    struct in_addr server_addr;
528    struct in_addr src_addr, src_mask;
529    struct in_addr dst_addr, dst_mask;
530    struct proxy_entry *proxy_entry;
531
532/* Copy command line into a buffer */
533    cmd_len = strlen(cmd);
534    if (cmd_len > (sizeof(buffer) - 1))
535        return -1;
536    strcpy(buffer, cmd);
537
538/* Convert to lower case */
539    len = strlen(buffer);
540    for (i=0; i<len; i++)
541        buffer[i] = tolower(buffer[i]);
542
543/* Set default proxy type */
544
545/* Set up default values */
546    rule_index = 0;
547    proxy_type = PROXY_TYPE_ENCODE_NONE;
548    proto = IPPROTO_TCP;
549    proxy_port = 0;
550    server_addr.s_addr = 0;
551    server_port = 0;
552    src_addr.s_addr = 0;
553    IpMask(0, &src_mask);
554    dst_addr.s_addr = 0;
555    IpMask(0, &dst_mask);
556
557    str_port[0] = 0;
558    str_server_port[0] = 0;
559
560/* Parse command string with state machine */
561#define STATE_READ_KEYWORD    0
562#define STATE_READ_TYPE       1
563#define STATE_READ_PORT       2
564#define STATE_READ_SERVER     3
565#define STATE_READ_RULE       4
566#define STATE_READ_DELETE     5
567#define STATE_READ_PROTO      6
568#define STATE_READ_SRC        7
569#define STATE_READ_DST        8
570    state = STATE_READ_KEYWORD;
571    token = strtok(buffer, " \t");
572    token_count = 0;
573    while (token != NULL)
574    {
575        token_count++;
576        switch (state)
577        {
578        case STATE_READ_KEYWORD:
579            if (strcmp(token, "type") == 0)
580                state = STATE_READ_TYPE;
581            else if (strcmp(token, "port") == 0)
582                state = STATE_READ_PORT;
583            else if (strcmp(token, "server") == 0)
584                state = STATE_READ_SERVER;
585            else if (strcmp(token, "rule") == 0)
586                state = STATE_READ_RULE;
587            else if (strcmp(token, "delete") == 0)
588                state = STATE_READ_DELETE;
589            else if (strcmp(token, "proto") == 0)
590                state = STATE_READ_PROTO;
591            else if (strcmp(token, "src") == 0)
592                state = STATE_READ_SRC;
593            else if (strcmp(token, "dst") == 0)
594                state = STATE_READ_DST;
595            else
596                return -1;
597            break;
598
599        case STATE_READ_TYPE:
600            if (strcmp(token, "encode_ip_hdr") == 0)
601                proxy_type = PROXY_TYPE_ENCODE_IPHDR;
602            else if (strcmp(token, "encode_tcp_stream") == 0)
603                proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
604            else if (strcmp(token, "no_encode") == 0)
605                proxy_type = PROXY_TYPE_ENCODE_NONE;
606            else
607                return -1;
608            state = STATE_READ_KEYWORD;
609            break;
610
611        case STATE_READ_PORT:
612            strcpy(str_port, token);
613            state = STATE_READ_KEYWORD;
614            break;
615
616        case STATE_READ_SERVER:
617            {
618                int err;
619                char *p;
620                char s[sizeof(buffer)];
621
622                p = token;
623                while (*p != ':' && *p != 0)
624                    p++;
625
626                if (*p != ':')
627                {
628                    err = IpAddr(token, &server_addr);
629                    if (err)
630                        return -1;
631                }
632                else
633                {
634                    *p = ' ';
635
636                    n = sscanf(token, "%s %s", s, str_server_port);
637                    if (n != 2)
638                        return -1;
639
640                    err = IpAddr(s, &server_addr);
641                    if (err)
642                        return -1;
643                }
644            }
645            state = STATE_READ_KEYWORD;
646            break;
647
648        case STATE_READ_RULE:
649            n = sscanf(token, "%d", &rule_index);
650            if (n != 1 || rule_index < 0)
651                return -1;
652            state = STATE_READ_KEYWORD;
653            break;
654
655        case STATE_READ_DELETE:
656            {
657                int err;
658                int rule_to_delete;
659
660                if (token_count != 2)
661                    return -1;
662
663                n = sscanf(token, "%d", &rule_to_delete);
664                if (n != 1)
665                    return -1;
666                err = RuleNumberDelete(rule_to_delete);
667                if (err)
668                    return -1;
669                return 0;
670            }
671
672        case STATE_READ_PROTO:
673            if (strcmp(token, "tcp") == 0)
674                proto = IPPROTO_TCP;
675            else if (strcmp(token, "udp") == 0)
676                proto = IPPROTO_UDP;
677            else
678                return -1;
679            state = STATE_READ_KEYWORD;
680            break;
681
682        case STATE_READ_SRC:
683        case STATE_READ_DST:
684            {
685                int err;
686                char *p;
687                struct in_addr mask;
688                struct in_addr addr;
689
690                p = token;
691                while (*p != '/' && *p != 0)
692                    p++;
693
694                if (*p != '/')
695                {
696                     IpMask(32, &mask);
697                     err = IpAddr(token, &addr);
698                     if (err)
699                         return -1;
700                }
701                else
702                {
703                    int n;
704                    int nbits;
705                    char s[sizeof(buffer)];
706
707                    *p = ' ';
708                    n = sscanf(token, "%s %d", s, &nbits);
709                    if (n != 2)
710                        return -1;
711
712                    err = IpAddr(s, &addr);
713                    if (err)
714                        return -1;
715
716                    err = IpMask(nbits, &mask);
717                    if (err)
718                        return -1;
719                }
720
721                if (state == STATE_READ_SRC)
722                {
723                    src_addr = addr;
724                    src_mask = mask;
725                }
726                else
727                {
728                    dst_addr = addr;
729                    dst_mask = mask;
730                }
731            }
732            state = STATE_READ_KEYWORD;
733            break;
734
735        default:
736            return -1;
737            break;
738        }
739
740        token = strtok(NULL, " \t");
741    }
742#undef STATE_READ_KEYWORD
743#undef STATE_READ_TYPE
744#undef STATE_READ_PORT
745#undef STATE_READ_SERVER
746#undef STATE_READ_RULE
747#undef STATE_READ_DELETE
748#undef STATE_READ_PROTO
749#undef STATE_READ_SRC
750#undef STATE_READ_DST
751
752/* Convert port strings to numbers.  This needs to be done after
753   the string is parsed, because the prototype might not be designated
754   before the ports (which might be symbolic entries in /etc/services) */
755
756    if (strlen(str_port) != 0)
757    {
758        int err;
759
760        err = IpPort(str_port, proto, &proxy_port);
761        if (err)
762            return -1;
763    }
764    else
765    {
766        proxy_port = 0;
767    }
768
769    if (strlen(str_server_port) != 0)
770    {
771        int err;
772
773        err = IpPort(str_server_port, proto, &server_port);
774        if (err)
775            return -1;
776    }
777    else
778    {
779        server_port = 0;
780    }
781
782/* Check that at least the server address has been defined */
783    if (server_addr.s_addr == 0)
784        return -1;
785
786/* Add to linked list */
787    proxy_entry = malloc(sizeof(struct proxy_entry));
788    if (proxy_entry == NULL)
789        return -1;
790
791    proxy_entry->proxy_type = proxy_type;
792    proxy_entry->rule_index = rule_index;
793    proxy_entry->proto = proto;
794    proxy_entry->proxy_port = htons(proxy_port);
795    proxy_entry->server_port = htons(server_port);
796    proxy_entry->server_addr = server_addr;
797    proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
798    proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
799    proxy_entry->src_mask = src_mask;
800    proxy_entry->dst_mask = dst_mask;
801
802    RuleAdd(proxy_entry);
803
804    return 0;
805}
806