xref: /freebsd/sys/netinet/libalias/alias_proxy.c (revision 719f9de58da52809811df0bb1e15b190eafba197)
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  */
58 struct 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 
87 static 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 
109 static int IpMask(int, struct in_addr *);
110 static int IpAddr(char *, struct in_addr *);
111 static int IpPort(char *, int, int *);
112 static void RuleAdd(struct proxy_entry *);
113 static void RuleDelete(struct proxy_entry *);
114 static int RuleNumberDelete(int);
115 static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
116 static void ProxyEncodeIpHeader(struct ip *, int);
117 
118 static int
119 IpMask(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 
135 static int
136 IpAddr(char *s, struct in_addr *addr)
137 {
138     if (inet_aton(s, addr) == 0)
139         return -1;
140     else
141         return 0;
142 }
143 
144 static int
145 IpPort(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 
170 void
171 RuleAdd(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 
216 static void
217 RuleDelete(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 
230 static int
231 RuleNumberDelete(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 
255 static void
256 ProxyEncodeTcpStream(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 
341 static void
342 ProxyEncodeIpHeader(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 fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
351 fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
352 
353 /* Check to see that there is room to add an IP option */
354     if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
355         return;
356 
357 /* Build option and copy into packet */
358     {
359         u_char *ptr;
360         struct tcphdr *tc;
361 
362         ptr = (u_char *) pip;
363         ptr += 20;
364         memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
365 
366         option[0] = 0x64; /* class: 3 (reserved), option 4 */
367         option[1] = OPTION_LEN_BYTES;
368 
369         memcpy(&option[2], (u_char *) &pip->ip_dst, 4);
370 
371         tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
372         memcpy(&option[6], (u_char *) &tc->th_sport, 2);
373 
374         memcpy(ptr, option, 8);
375     }
376 
377 /* Update checksum, header length and packet length */
378     {
379         int i;
380         int accumulate;
381         u_short *sptr;
382 
383         sptr = (u_short *) option;
384         accumulate = 0;
385         for (i=0; i<OPTION_LEN_INT16; i++)
386             accumulate -= *(sptr++);
387 
388         sptr = (u_short *) pip;
389         accumulate += *sptr;
390         pip->ip_hl += OPTION_LEN_INT32;
391         accumulate -= *sptr;
392 
393         accumulate += pip->ip_len;
394         pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
395         accumulate -= pip->ip_len;
396 
397         ADJUST_CHECKSUM(accumulate, pip->ip_sum);
398     }
399 #undef OPTION_LEN_BYTES
400 #undef OPTION_LEN_INT16
401 #undef OPTION_LEN_INT32
402 fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
403 fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
404 }
405 
406 
407 /* Functions by other packet alias source files
408 
409     ProxyCheck()         -- Checks whether an outgoing packet should
410                             be proxied.
411     ProxyModify()        -- Encodes the original destination address/port
412                             for a packet which is to be redirected to
413                             a proxy server.
414 */
415 
416 int
417 ProxyCheck(struct ip *pip,
418            struct in_addr *proxy_server_addr,
419            u_short *proxy_server_port)
420 {
421     u_short dst_port;
422     struct in_addr src_addr;
423     struct in_addr dst_addr;
424     struct proxy_entry *ptr;
425 
426     src_addr = pip->ip_src;
427     dst_addr = pip->ip_dst;
428     dst_port = ((struct tcphdr *) ((char *) pip + (pip->ip_hl << 2)))
429         ->th_dport;
430 
431     ptr = proxyList;
432     while (ptr != NULL)
433     {
434         u_short proxy_port;
435 
436         proxy_port = ptr->proxy_port;
437         if ((dst_port == proxy_port || proxy_port == 0)
438          && pip->ip_p == ptr->proto
439          && src_addr.s_addr != ptr->server_addr.s_addr)
440         {
441             struct in_addr src_addr_masked;
442             struct in_addr dst_addr_masked;
443 
444             src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
445             dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
446 
447             if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
448              && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr))
449             {
450                 if ((*proxy_server_port = ptr->server_port) == 0)
451                     *proxy_server_port = dst_port;
452                 *proxy_server_addr = ptr->server_addr;
453                 return ptr->proxy_type;
454             }
455         }
456         ptr = ptr->next;
457     }
458 
459     return 0;
460 }
461 
462 void
463 ProxyModify(struct alias_link *link,
464             struct ip *pip,
465             int maxpacketsize,
466             int proxy_type)
467 {
468     switch (proxy_type)
469     {
470     case PROXY_TYPE_ENCODE_IPHDR:
471         ProxyEncodeIpHeader(pip, maxpacketsize);
472         break;
473 
474     case PROXY_TYPE_ENCODE_TCPSTREAM:
475         ProxyEncodeTcpStream(link, pip, maxpacketsize);
476         break;
477     }
478 }
479 
480 
481 /*
482     Public API functions
483 */
484 
485 int
486 PacketAliasProxyRule(char *cmd)
487 {
488 /*
489  * This function takes command strings of the form:
490  *
491  *   server <addr>[:<port>]
492  *   [port <port>]
493  *   [rule n]
494  *   [proto tcp|udp]
495  *   [src <addr>[/n]]
496  *   [dst <addr>[/n]]
497  *   [type encode_tcp_stream|encode_ip_hdr|no_encode]
498  *
499  *   delete <rule number>
500  *
501  * Subfields can be in arbitrary order.  Port numbers and addresses
502  * must be in either numeric or symbolic form. An optional rule number
503  * is used to control the order in which rules are searched.  If two
504  * rules have the same number, then search order cannot be guaranteed,
505  * and the rules should be disjoint.  If no rule number is specified,
506  * then 0 is used, and group 0 rules are always checked before any
507  * others.
508  */
509     int i, n, len;
510     int cmd_len;
511     int token_count;
512     int state;
513     char *token;
514     char buffer[256];
515     char str_port[sizeof(buffer)];
516     char str_server_port[sizeof(buffer)];
517 
518     int rule_index;
519     int proto;
520     int proxy_type;
521     int proxy_port;
522     int server_port;
523     struct in_addr server_addr;
524     struct in_addr src_addr, src_mask;
525     struct in_addr dst_addr, dst_mask;
526     struct proxy_entry *proxy_entry;
527 
528 /* Copy command line into a buffer */
529     cmd_len = strlen(cmd);
530     if (cmd_len > (sizeof(buffer) - 1))
531         return -1;
532     strcpy(buffer, cmd);
533 
534 /* Convert to lower case */
535     len = strlen(buffer);
536     for (i=0; i<len; i++)
537         buffer[i] = tolower(buffer[i]);
538 
539 /* Set default proxy type */
540 
541 /* Set up default values */
542     rule_index = 0;
543     proxy_type = PROXY_TYPE_ENCODE_NONE;
544     proto = IPPROTO_TCP;
545     proxy_port = 0;
546     server_addr.s_addr = 0;
547     server_port = 0;
548     src_addr.s_addr = 0;
549     IpMask(0, &src_mask);
550     dst_addr.s_addr = 0;
551     IpMask(0, &dst_mask);
552 
553     str_port[0] = 0;
554     str_server_port[0] = 0;
555 
556 /* Parse command string with state machine */
557 #define STATE_READ_KEYWORD    0
558 #define STATE_READ_TYPE       1
559 #define STATE_READ_PORT       2
560 #define STATE_READ_SERVER     3
561 #define STATE_READ_RULE       4
562 #define STATE_READ_DELETE     5
563 #define STATE_READ_PROTO      6
564 #define STATE_READ_SRC        7
565 #define STATE_READ_DST        8
566     state = STATE_READ_KEYWORD;
567     token = strtok(buffer, " \t");
568     token_count = 0;
569     while (token != NULL)
570     {
571         token_count++;
572         switch (state)
573         {
574         case STATE_READ_KEYWORD:
575             if (strcmp(token, "type") == 0)
576                 state = STATE_READ_TYPE;
577             else if (strcmp(token, "port") == 0)
578                 state = STATE_READ_PORT;
579             else if (strcmp(token, "server") == 0)
580                 state = STATE_READ_SERVER;
581             else if (strcmp(token, "rule") == 0)
582                 state = STATE_READ_RULE;
583             else if (strcmp(token, "delete") == 0)
584                 state = STATE_READ_DELETE;
585             else if (strcmp(token, "proto") == 0)
586                 state = STATE_READ_PROTO;
587             else if (strcmp(token, "src") == 0)
588                 state = STATE_READ_SRC;
589             else if (strcmp(token, "dst") == 0)
590                 state = STATE_READ_DST;
591             else
592                 return -1;
593             break;
594 
595         case STATE_READ_TYPE:
596             if (strcmp(token, "encode_ip_hdr") == 0)
597                 proxy_type = PROXY_TYPE_ENCODE_IPHDR;
598             else if (strcmp(token, "encode_tcp_stream") == 0)
599                 proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
600             else if (strcmp(token, "no_encode") == 0)
601                 proxy_type = PROXY_TYPE_ENCODE_NONE;
602             else
603                 return -1;
604             state = STATE_READ_KEYWORD;
605             break;
606 
607         case STATE_READ_PORT:
608             strcpy(str_port, token);
609             state = STATE_READ_KEYWORD;
610             break;
611 
612         case STATE_READ_SERVER:
613             {
614                 int err;
615                 char *p;
616                 char s[sizeof(buffer)];
617 
618                 p = token;
619                 while (*p != ':' && *p != 0)
620                     p++;
621 
622                 if (*p != ':')
623                 {
624                     err = IpAddr(token, &server_addr);
625                     if (err)
626                         return -1;
627                 }
628                 else
629                 {
630                     *p = ' ';
631 
632                     n = sscanf(token, "%s %s", s, str_server_port);
633                     if (n != 2)
634                         return -1;
635 
636                     err = IpAddr(s, &server_addr);
637                     if (err)
638                         return -1;
639                 }
640             }
641             state = STATE_READ_KEYWORD;
642             break;
643 
644         case STATE_READ_RULE:
645             n = sscanf(token, "%d", &rule_index);
646             if (n != 1 || rule_index < 0)
647                 return -1;
648             state = STATE_READ_KEYWORD;
649             break;
650 
651         case STATE_READ_DELETE:
652             {
653                 int err;
654                 int rule_to_delete;
655 
656                 if (token_count != 2)
657                     return -1;
658 
659                 n = sscanf(token, "%d", &rule_to_delete);
660                 if (n != 1)
661                     return -1;
662                 err = RuleNumberDelete(rule_to_delete);
663                 if (err)
664                     return -1;
665                 return 0;
666             }
667 
668         case STATE_READ_PROTO:
669             if (strcmp(token, "tcp") == 0)
670                 proto = IPPROTO_TCP;
671             else if (strcmp(token, "udp") == 0)
672                 proto = IPPROTO_UDP;
673             else
674                 return -1;
675             state = STATE_READ_KEYWORD;
676             break;
677 
678         case STATE_READ_SRC:
679         case STATE_READ_DST:
680             {
681                 int err;
682                 char *p;
683                 struct in_addr mask;
684                 struct in_addr addr;
685 
686                 p = token;
687                 while (*p != '/' && *p != 0)
688                     p++;
689 
690                 if (*p != '/')
691                 {
692                      IpMask(32, &mask);
693                      err = IpAddr(token, &addr);
694                      if (err)
695                          return -1;
696                 }
697                 else
698                 {
699                     int n;
700                     int nbits;
701                     char s[sizeof(buffer)];
702 
703                     *p = ' ';
704                     n = sscanf(token, "%s %d", s, &nbits);
705                     if (n != 2)
706                         return -1;
707 
708                     err = IpAddr(s, &addr);
709                     if (err)
710                         return -1;
711 
712                     err = IpMask(nbits, &mask);
713                     if (err)
714                         return -1;
715                 }
716 
717                 if (state == STATE_READ_SRC)
718                 {
719                     src_addr = addr;
720                     src_mask = mask;
721                 }
722                 else
723                 {
724                     dst_addr = addr;
725                     dst_mask = mask;
726                 }
727             }
728             state = STATE_READ_KEYWORD;
729             break;
730 
731         default:
732             return -1;
733             break;
734         }
735 
736         token = strtok(NULL, " \t");
737     }
738 #undef STATE_READ_KEYWORD
739 #undef STATE_READ_TYPE
740 #undef STATE_READ_PORT
741 #undef STATE_READ_SERVER
742 #undef STATE_READ_RULE
743 #undef STATE_READ_DELETE
744 #undef STATE_READ_PROTO
745 #undef STATE_READ_SRC
746 #undef STATE_READ_DST
747 
748 /* Convert port strings to numbers.  This needs to be done after
749    the string is parsed, because the prototype might not be designated
750    before the ports (which might be symbolic entries in /etc/services) */
751 
752     if (strlen(str_port) != 0)
753     {
754         int err;
755 
756         err = IpPort(str_port, proto, &proxy_port);
757         if (err)
758             return -1;
759     }
760     else
761     {
762         proxy_port = 0;
763     }
764 
765     if (strlen(str_server_port) != 0)
766     {
767         int err;
768 
769         err = IpPort(str_server_port, proto, &server_port);
770         if (err)
771             return -1;
772     }
773     else
774     {
775         server_port = 0;
776     }
777 
778 /* Check that at least the server address has been defined */
779     if (server_addr.s_addr == 0)
780         return -1;
781 
782 /* Add to linked list */
783     proxy_entry = malloc(sizeof(struct proxy_entry));
784     if (proxy_entry == NULL)
785         return -1;
786 
787     proxy_entry->proxy_type = proxy_type;
788     proxy_entry->rule_index = rule_index;
789     proxy_entry->proto = proto;
790     proxy_entry->proxy_port = htons(proxy_port);
791     proxy_entry->server_port = htons(server_port);
792     proxy_entry->server_addr = server_addr;
793     proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
794     proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
795     proxy_entry->src_mask = src_mask;
796     proxy_entry->dst_mask = dst_mask;
797 
798     RuleAdd(proxy_entry);
799 
800     return 0;
801 }
802