xref: /freebsd/sys/netinet/libalias/alias_db.c (revision d1ba25f456132eabc6f1244e4bbbf3d19e8f3a31)
1 /*  -*- mode: c; tab-width: 8; c-basic-indent: 4; -*-
2     Alias_db.c encapsulates all data structures used for storing
3     packet aliasing data.  Other parts of the aliasing software
4     access data through functions provided in this file.
5 
6     Data storage is based on the notion of a "link", which is
7     established for ICMP echo/reply packets, UDP datagrams and
8     TCP stream connections.  A link stores the original source
9     and destination addresses.  For UDP and TCP, it also stores
10     source and destination port numbers, as well as an alias
11     port number.  Links are also used to store information about
12     fragments.
13 
14     There is a facility for sweeping through and deleting old
15     links as new packets are sent through.  A simple timeout is
16     used for ICMP and UDP links.  TCP links are left alone unless
17     there is an incomplete connection, in which case the link
18     can be deleted after a certain amount of time.
19 
20 
21     This software is placed into the public domain with no restrictions
22     on its distribution.
23 
24     Initial version: August, 1996  (cjm)
25 
26     Version 1.4: September 16, 1996 (cjm)
27         Facility for handling incoming links added.
28 
29     Version 1.6: September 18, 1996 (cjm)
30         ICMP data handling simplified.
31 
32     Version 1.7: January 9, 1997 (cjm)
33         Fragment handling simplified.
34         Saves pointers for unresolved fragments.
35         Permits links for unspecified remote ports
36           or unspecified remote addresses.
37         Fixed bug which did not properly zero port
38           table entries after a link was deleted.
39         Cleaned up some obsolete comments.
40 
41     Version 1.8: January 14, 1997 (cjm)
42         Fixed data type error in StartPoint().
43         (This error did not exist prior to v1.7
44         and was discovered and fixed by Ari Suutari)
45 
46     Version 1.9: February 1, 1997
47         Optionally, connections initiated from packet aliasing host
48         machine will will not have their port number aliased unless it
49         conflicts with an aliasing port already being used. (cjm)
50 
51         All options earlier being #ifdef'ed are now available through
52         a new interface, SetPacketAliasMode().  This allows run time
53         control (which is now available in PPP+pktAlias through the
54         'alias' keyword). (ee)
55 
56         Added ability to create an alias port without
57         either destination address or port specified.
58         port type = ALIAS_PORT_UNKNOWN_DEST_ALL (ee)
59 
60         Removed K&R style function headers
61         and general cleanup. (ee)
62 
63         Added packetAliasMode to replace compiler #defines's (ee)
64 
65         Allocates sockets for partially specified
66         ports if ALIAS_USE_SOCKETS defined. (cjm)
67 
68     Version 2.0: March, 1997
69         SetAliasAddress() will now clean up alias links
70         if the aliasing address is changed. (cjm)
71 
72         PacketAliasPermanentLink() function added to support permanent
73         links.  (J. Fortes suggested the need for this.)
74         Examples:
75 
76         (192.168.0.1, port 23)  <-> alias port 6002, unknown dest addr/port
77 
78         (192.168.0.2, port 21)  <-> alias port 3604, known dest addr
79                                                      unknown dest port
80 
81         These permanent links allow for incoming connections to
82         machines on the local network.  They can be given with a
83         user-chosen amount of specificity, with increasing specificity
84         meaning more security. (cjm)
85 
86         Quite a bit of rework to the basic engine.  The portTable[]
87         array, which kept track of which ports were in use was replaced
88         by a table/linked list structure. (cjm)
89 
90         SetExpire() function added. (cjm)
91 
92         DeleteLink() no longer frees memory association with a pointer
93         to a fragment (this bug was first recognized by E. Eklund in
94         v1.9).
95 
96     Version 2.1: May, 1997 (cjm)
97         Packet aliasing engine reworked so that it can handle
98         multiple external addresses rather than just a single
99         host address.
100 
101         PacketAliasRedirectPort() and PacketAliasRedirectAddr()
102         added to the API.  The first function is a more generalized
103         version of PacketAliasPermanentLink().  The second function
104         implements static network address translation.
105 
106     Version 3.2: July, 2000 (salander and satoh)
107         Added FindNewPortGroup to get contiguous range of port values.
108 
109         Added QueryUdpTcpIn and QueryUdpTcpOut to look for an aliasing
110 	link but not actually add one.
111 
112         Added FindRtspOut, which is closely derived from FindUdpTcpOut,
113 	except that the alias port (from FindNewPortGroup) is provided
114 	as input.
115 
116     See HISTORY file for additional revisions.
117 
118     $FreeBSD$
119 */
120 
121 
122 /* System include files */
123 #include <errno.h>
124 #include <stdlib.h>
125 #include <stdio.h>
126 #include <unistd.h>
127 
128 #include <sys/queue.h>
129 #include <sys/socket.h>
130 #include <sys/time.h>
131 #include <sys/types.h>
132 
133 /* BSD network include files */
134 #include <netinet/in_systm.h>
135 #include <netinet/in.h>
136 #include <netinet/ip.h>
137 #include <netinet/tcp.h>
138 #include <arpa/inet.h>
139 
140 #include "alias.h"
141 #include "alias_local.h"
142 
143 
144 
145 /*
146    Constants (note: constants are also defined
147               near relevant functions or structs)
148 */
149 
150 /* Sizes of input and output link tables */
151 #define LINK_TABLE_OUT_SIZE         101
152 #define LINK_TABLE_IN_SIZE         4001
153 
154 /* Parameters used for cleanup of expired links */
155 #define ALIAS_CLEANUP_INTERVAL_SECS  60
156 #define ALIAS_CLEANUP_MAX_SPOKES     30
157 
158 /* Timeouts (in seconds) for different link types */
159 #define ICMP_EXPIRE_TIME             60
160 #define UDP_EXPIRE_TIME              60
161 #define PPTP_EXPIRE_TIME             60
162 #define PROTO_EXPIRE_TIME            60
163 #define FRAGMENT_ID_EXPIRE_TIME      10
164 #define FRAGMENT_PTR_EXPIRE_TIME     30
165 
166 /* TCP link expire time for different cases */
167 /* When the link has been used and closed - minimal grace time to
168    allow ACKs and potential re-connect in FTP (XXX - is this allowed?)  */
169 #ifndef TCP_EXPIRE_DEAD
170 #   define TCP_EXPIRE_DEAD           10
171 #endif
172 
173 /* When the link has been used and closed on one side - the other side
174    is allowed to still send data */
175 #ifndef TCP_EXPIRE_SINGLEDEAD
176 #   define TCP_EXPIRE_SINGLEDEAD     90
177 #endif
178 
179 /* When the link isn't yet up */
180 #ifndef TCP_EXPIRE_INITIAL
181 #   define TCP_EXPIRE_INITIAL       300
182 #endif
183 
184 /* When the link is up */
185 #ifndef TCP_EXPIRE_CONNECTED
186 #   define TCP_EXPIRE_CONNECTED   86400
187 #endif
188 
189 
190 /* Dummy port number codes used for FindLinkIn/Out() and AddLink().
191    These constants can be anything except zero, which indicates an
192    unknown port number. */
193 
194 #define NO_DEST_PORT     1
195 #define NO_SRC_PORT      1
196 
197 
198 
199 /* Data Structures
200 
201     The fundamental data structure used in this program is
202     "struct alias_link".  Whenever a TCP connection is made,
203     a UDP datagram is sent out, or an ICMP echo request is made,
204     a link record is made (if it has not already been created).
205     The link record is identified by the source address/port
206     and the destination address/port. In the case of an ICMP
207     echo request, the source port is treated as being equivalent
208     with the 16-bit ID number of the ICMP packet.
209 
210     The link record also can store some auxiliary data.  For
211     TCP connections that have had sequence and acknowledgment
212     modifications, data space is available to track these changes.
213     A state field is used to keep track in changes to the TCP
214     connection state.  ID numbers of fragments can also be
215     stored in the auxiliary space.  Pointers to unresolved
216     fragments can also be stored.
217 
218     The link records support two independent chainings.  Lookup
219     tables for input and out tables hold the initial pointers
220     the link chains.  On input, the lookup table indexes on alias
221     port and link type.  On output, the lookup table indexes on
222     source address, destination address, source port, destination
223     port and link type.
224 */
225 
226 struct ack_data_record     /* used to save changes to ACK/sequence numbers */
227 {
228     u_long ack_old;
229     u_long ack_new;
230     int delta;
231     int active;
232 };
233 
234 struct tcp_state           /* Information about TCP connection        */
235 {
236     int in;                /* State for outside -> inside             */
237     int out;               /* State for inside  -> outside            */
238     int index;             /* Index to ACK data array                 */
239     int ack_modified;      /* Indicates whether ACK and sequence numbers */
240                            /* been modified                           */
241 };
242 
243 #define N_LINK_TCP_DATA   3 /* Number of distinct ACK number changes
244                                saved for a modified TCP stream */
245 struct tcp_dat
246 {
247     struct tcp_state state;
248     struct ack_data_record ack[N_LINK_TCP_DATA];
249     int fwhole;             /* Which firewall record is used for this hole? */
250 };
251 
252 struct server              /* LSNAT server pool (circular list) */
253 {
254     struct in_addr addr;
255     u_short port;
256     struct server *next;
257 };
258 
259 struct alias_link                /* Main data structure */
260 {
261     struct in_addr src_addr;     /* Address and port information        */
262     struct in_addr dst_addr;
263     struct in_addr alias_addr;
264     struct in_addr proxy_addr;
265     u_short src_port;
266     u_short dst_port;
267     u_short alias_port;
268     u_short proxy_port;
269     struct server *server;
270 
271     int link_type;               /* Type of link: TCP, UDP, ICMP, proto, frag */
272 
273 /* values for link_type */
274 #define LINK_ICMP                     IPPROTO_ICMP
275 #define LINK_UDP                      IPPROTO_UDP
276 #define LINK_TCP                      IPPROTO_TCP
277 #define LINK_FRAGMENT_ID              (IPPROTO_MAX + 1)
278 #define LINK_FRAGMENT_PTR             (IPPROTO_MAX + 2)
279 #define LINK_ADDR                     (IPPROTO_MAX + 3)
280 #define LINK_PPTP                     (IPPROTO_MAX + 4)
281 
282     int flags;                   /* indicates special characteristics   */
283 
284 /* flag bits */
285 #define LINK_UNKNOWN_DEST_PORT     0x01
286 #define LINK_UNKNOWN_DEST_ADDR     0x02
287 #define LINK_PERMANENT             0x04
288 #define LINK_PARTIALLY_SPECIFIED   0x03 /* logical-or of first two bits */
289 #define LINK_UNFIREWALLED          0x08
290 #define LINK_LAST_LINE_CRLF_TERMED 0x10
291 
292     int timestamp;               /* Time link was last accessed         */
293     int expire_time;             /* Expire time for link                */
294 
295     int sockfd;                  /* socket descriptor                   */
296 
297     LIST_ENTRY(alias_link) list_out; /* Linked list of pointers for     */
298     LIST_ENTRY(alias_link) list_in;  /* input and output lookup tables  */
299 
300     union                        /* Auxiliary data                      */
301     {
302         char *frag_ptr;
303         struct in_addr frag_addr;
304         struct tcp_dat *tcp;
305     } data;
306 };
307 
308 
309 
310 
311 
312 /* Global Variables
313 
314     The global variables listed here are only accessed from
315     within alias_db.c and so are prefixed with the static
316     designation.
317 */
318 
319 int packetAliasMode;                 /* Mode flags                      */
320                                      /*        - documented in alias.h  */
321 
322 static struct in_addr aliasAddress;  /* Address written onto source     */
323                                      /*   field of IP packet.           */
324 
325 static struct in_addr targetAddress; /* IP address incoming packets     */
326                                      /*   are sent to if no aliasing    */
327                                      /*   link already exists           */
328 
329 static struct in_addr nullAddress;   /* Used as a dummy parameter for   */
330                                      /*   some function calls           */
331 static LIST_HEAD(, alias_link)
332 linkTableOut[LINK_TABLE_OUT_SIZE];   /* Lookup table of pointers to     */
333                                      /*   chains of link records. Each  */
334 static LIST_HEAD(, alias_link)       /*   link record is doubly indexed */
335 linkTableIn[LINK_TABLE_IN_SIZE];     /*   into input and output lookup  */
336                                      /*   tables.                       */
337 
338 static int icmpLinkCount;            /* Link statistics                 */
339 static int udpLinkCount;
340 static int tcpLinkCount;
341 static int pptpLinkCount;
342 static int protoLinkCount;
343 static int fragmentIdLinkCount;
344 static int fragmentPtrLinkCount;
345 static int sockCount;
346 
347 static int cleanupIndex;             /* Index to chain of link table    */
348                                      /* being inspected for old links   */
349 
350 static int timeStamp;                /* System time in seconds for      */
351                                      /* current packet                  */
352 
353 static int lastCleanupTime;          /* Last time IncrementalCleanup()  */
354                                      /* was called                      */
355 
356 static int houseKeepingResidual;     /* used by HouseKeeping()          */
357 
358 static int deleteAllLinks;           /* If equal to zero, DeleteLink()  */
359                                      /* will not remove permanent links */
360 
361 static FILE *monitorFile;            /* File descriptor for link        */
362                                      /* statistics monitoring file      */
363 
364 static int newDefaultLink;           /* Indicates if a new aliasing     */
365                                      /* link has been created after a   */
366                                      /* call to PacketAliasIn/Out().    */
367 
368 #ifndef NO_FW_PUNCH
369 static int fireWallFD = -1;          /* File descriptor to be able to   */
370                                      /* control firewall.  Opened by    */
371                                      /* PacketAliasSetMode on first     */
372                                      /* setting the PKT_ALIAS_PUNCH_FW  */
373                                      /* flag.                           */
374 #endif
375 
376 
377 
378 
379 
380 
381 
382 /* Internal utility routines (used only in alias_db.c)
383 
384 Lookup table starting points:
385     StartPointIn()           -- link table initial search point for
386                                 incoming packets
387     StartPointOut()          -- link table initial search point for
388                                 outgoing packets
389 
390 Miscellaneous:
391     SeqDiff()                -- difference between two TCP sequences
392     ShowAliasStats()         -- send alias statistics to a monitor file
393 */
394 
395 
396 /* Local prototypes */
397 static u_int StartPointIn(struct in_addr, u_short, int);
398 
399 static u_int StartPointOut(struct in_addr, struct in_addr,
400                            u_short, u_short, int);
401 
402 static int SeqDiff(u_long, u_long);
403 
404 static void ShowAliasStats(void);
405 
406 #ifndef NO_FW_PUNCH
407 /* Firewall control */
408 static void InitPunchFW(void);
409 static void UninitPunchFW(void);
410 static void ClearFWHole(struct alias_link *link);
411 #endif
412 
413 /* Log file control */
414 static void InitPacketAliasLog(void);
415 static void UninitPacketAliasLog(void);
416 
417 static u_int
418 StartPointIn(struct in_addr alias_addr,
419              u_short alias_port,
420              int link_type)
421 {
422     u_int n;
423 
424     n  = alias_addr.s_addr;
425     n += alias_port;
426     n += link_type;
427     return(n % LINK_TABLE_IN_SIZE);
428 }
429 
430 
431 static u_int
432 StartPointOut(struct in_addr src_addr, struct in_addr dst_addr,
433               u_short src_port, u_short dst_port, int link_type)
434 {
435     u_int n;
436 
437     n  = src_addr.s_addr;
438     n += dst_addr.s_addr;
439     n += src_port;
440     n += dst_port;
441     n += link_type;
442 
443     return(n % LINK_TABLE_OUT_SIZE);
444 }
445 
446 
447 static int
448 SeqDiff(u_long x, u_long y)
449 {
450 /* Return the difference between two TCP sequence numbers */
451 
452 /*
453     This function is encapsulated in case there are any unusual
454     arithmetic conditions that need to be considered.
455 */
456 
457     return (ntohl(y) - ntohl(x));
458 }
459 
460 
461 static void
462 ShowAliasStats(void)
463 {
464 /* Used for debugging */
465 
466    if (monitorFile)
467    {
468       fprintf(monitorFile, "icmp=%d, udp=%d, tcp=%d, pptp=%d, proto=%d, frag_id=%d frag_ptr=%d",
469               icmpLinkCount,
470               udpLinkCount,
471               tcpLinkCount,
472               pptpLinkCount,
473               protoLinkCount,
474               fragmentIdLinkCount,
475               fragmentPtrLinkCount);
476 
477       fprintf(monitorFile, " / tot=%d  (sock=%d)\n",
478               icmpLinkCount + udpLinkCount
479                             + tcpLinkCount
480                             + pptpLinkCount
481                             + protoLinkCount
482                             + fragmentIdLinkCount
483                             + fragmentPtrLinkCount,
484               sockCount);
485 
486       fflush(monitorFile);
487    }
488 }
489 
490 
491 
492 
493 
494 /* Internal routines for finding, deleting and adding links
495 
496 Port Allocation:
497     GetNewPort()             -- find and reserve new alias port number
498     GetSocket()              -- try to allocate a socket for a given port
499 
500 Link creation and deletion:
501     CleanupAliasData()      - remove all link chains from lookup table
502     IncrementalCleanup()    - look for stale links in a single chain
503     DeleteLink()            - remove link
504     AddLink()               - add link
505     ReLink()                - change link
506 
507 Link search:
508     FindLinkOut()           - find link for outgoing packets
509     FindLinkIn()            - find link for incoming packets
510 
511 Port search:
512     FindNewPortGroup()      - find an available group of ports
513 */
514 
515 /* Local prototypes */
516 static int GetNewPort(struct alias_link *, int);
517 
518 static u_short GetSocket(u_short, int *, int);
519 
520 static void CleanupAliasData(void);
521 
522 static void IncrementalCleanup(void);
523 
524 static void DeleteLink(struct alias_link *);
525 
526 static struct alias_link *
527 AddLink(struct in_addr, struct in_addr, struct in_addr,
528         u_short, u_short, int, int);
529 
530 static struct alias_link *
531 ReLink(struct alias_link *,
532        struct in_addr, struct in_addr, struct in_addr,
533         u_short, u_short, int, int);
534 
535 static struct alias_link *
536 FindLinkOut(struct in_addr, struct in_addr, u_short, u_short, int, int);
537 
538 static struct alias_link *
539 FindLinkIn(struct in_addr, struct in_addr, u_short, u_short, int, int);
540 
541 
542 #define ALIAS_PORT_BASE            0x08000
543 #define ALIAS_PORT_MASK            0x07fff
544 #define ALIAS_PORT_MASK_EVEN       0x07ffe
545 #define GET_NEW_PORT_MAX_ATTEMPTS       20
546 
547 #define GET_ALIAS_PORT                  -1
548 #define GET_ALIAS_ID        GET_ALIAS_PORT
549 
550 #define FIND_EVEN_ALIAS_BASE             1
551 
552 /* GetNewPort() allocates port numbers.  Note that if a port number
553    is already in use, that does not mean that it cannot be used by
554    another link concurrently.  This is because GetNewPort() looks for
555    unused triplets: (dest addr, dest port, alias port). */
556 
557 static int
558 GetNewPort(struct alias_link *link, int alias_port_param)
559 {
560     int i;
561     int max_trials;
562     u_short port_sys;
563     u_short port_net;
564 
565 /*
566    Description of alias_port_param for GetNewPort().  When
567    this parameter is zero or positive, it precisely specifies
568    the port number.  GetNewPort() will return this number
569    without check that it is in use.
570 
571    When this parameter is GET_ALIAS_PORT, it indicates to get a randomly
572    selected port number.
573 */
574 
575     if (alias_port_param == GET_ALIAS_PORT)
576     {
577         /*
578          * The aliasing port is automatically selected
579          * by one of two methods below:
580          */
581         max_trials = GET_NEW_PORT_MAX_ATTEMPTS;
582 
583         if (packetAliasMode & PKT_ALIAS_SAME_PORTS)
584         {
585             /*
586              * When the PKT_ALIAS_SAME_PORTS option is
587              * chosen, the first try will be the
588              * actual source port. If this is already
589              * in use, the remainder of the trials
590              * will be random.
591              */
592             port_net = link->src_port;
593             port_sys = ntohs(port_net);
594         }
595         else
596         {
597             /* First trial and all subsequent are random. */
598             port_sys = random() & ALIAS_PORT_MASK;
599             port_sys += ALIAS_PORT_BASE;
600             port_net = htons(port_sys);
601         }
602     }
603     else if (alias_port_param >= 0 && alias_port_param < 0x10000)
604     {
605         link->alias_port = (u_short) alias_port_param;
606         return(0);
607     }
608     else
609     {
610 #ifdef DEBUG
611         fprintf(stderr, "PacketAlias/GetNewPort(): ");
612         fprintf(stderr, "input parameter error\n");
613 #endif
614         return(-1);
615     }
616 
617 
618 /* Port number search */
619     for (i=0; i<max_trials; i++)
620     {
621         int go_ahead;
622         struct alias_link *search_result;
623 
624         search_result = FindLinkIn(link->dst_addr, link->alias_addr,
625                                    link->dst_port, port_net,
626                                    link->link_type, 0);
627 
628         if (search_result == NULL)
629             go_ahead = 1;
630         else if (!(link->flags          & LINK_PARTIALLY_SPECIFIED)
631                && (search_result->flags & LINK_PARTIALLY_SPECIFIED))
632             go_ahead = 1;
633         else
634             go_ahead = 0;
635 
636         if (go_ahead)
637         {
638             if ((packetAliasMode & PKT_ALIAS_USE_SOCKETS)
639              && (link->flags & LINK_PARTIALLY_SPECIFIED)
640 	     && ((link->link_type == LINK_TCP) ||
641 		 (link->link_type == LINK_UDP)))
642             {
643                 if (GetSocket(port_net, &link->sockfd, link->link_type))
644                 {
645                     link->alias_port = port_net;
646                     return(0);
647                 }
648             }
649             else
650             {
651                 link->alias_port = port_net;
652                 return(0);
653             }
654         }
655 
656         port_sys = random() & ALIAS_PORT_MASK;
657         port_sys += ALIAS_PORT_BASE;
658         port_net = htons(port_sys);
659     }
660 
661 #ifdef DEBUG
662     fprintf(stderr, "PacketAlias/GetnewPort(): ");
663     fprintf(stderr, "could not find free port\n");
664 #endif
665 
666     return(-1);
667 }
668 
669 
670 static u_short
671 GetSocket(u_short port_net, int *sockfd, int link_type)
672 {
673     int err;
674     int sock;
675     struct sockaddr_in sock_addr;
676 
677     if (link_type == LINK_TCP)
678         sock = socket(AF_INET, SOCK_STREAM, 0);
679     else if (link_type == LINK_UDP)
680         sock = socket(AF_INET, SOCK_DGRAM, 0);
681     else
682     {
683 #ifdef DEBUG
684         fprintf(stderr, "PacketAlias/GetSocket(): ");
685         fprintf(stderr, "incorrect link type\n");
686 #endif
687         return(0);
688     }
689 
690     if (sock < 0)
691     {
692 #ifdef DEBUG
693         fprintf(stderr, "PacketAlias/GetSocket(): ");
694         fprintf(stderr, "socket() error %d\n", *sockfd);
695 #endif
696         return(0);
697     }
698 
699     sock_addr.sin_family = AF_INET;
700     sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
701     sock_addr.sin_port = port_net;
702 
703     err = bind(sock,
704                (struct sockaddr *) &sock_addr,
705                sizeof(sock_addr));
706     if (err == 0)
707     {
708         sockCount++;
709         *sockfd = sock;
710         return(1);
711     }
712     else
713     {
714         close(sock);
715         return(0);
716     }
717 }
718 
719 
720 /* FindNewPortGroup() returns a base port number for an available
721    range of contiguous port numbers. Note that if a port number
722    is already in use, that does not mean that it cannot be used by
723    another link concurrently.  This is because FindNewPortGroup()
724    looks for unused triplets: (dest addr, dest port, alias port). */
725 
726 int
727 FindNewPortGroup(struct in_addr  dst_addr,
728                  struct in_addr  alias_addr,
729                  u_short         src_port,
730                  u_short         dst_port,
731                  u_short         port_count,
732 		 u_char          proto,
733 		 u_char          align)
734 {
735     int     i, j;
736     int     max_trials;
737     u_short port_sys;
738     int     link_type;
739 
740     /*
741      * Get link_type from protocol
742      */
743 
744     switch (proto)
745     {
746     case IPPROTO_UDP:
747         link_type = LINK_UDP;
748         break;
749     case IPPROTO_TCP:
750         link_type = LINK_TCP;
751         break;
752     default:
753         return (0);
754         break;
755     }
756 
757     /*
758      * The aliasing port is automatically selected
759      * by one of two methods below:
760      */
761     max_trials = GET_NEW_PORT_MAX_ATTEMPTS;
762 
763     if (packetAliasMode & PKT_ALIAS_SAME_PORTS) {
764       /*
765        * When the ALIAS_SAME_PORTS option is
766        * chosen, the first try will be the
767        * actual source port. If this is already
768        * in use, the remainder of the trials
769        * will be random.
770        */
771       port_sys = ntohs(src_port);
772 
773     } else {
774 
775       /* First trial and all subsequent are random. */
776       if (align == FIND_EVEN_ALIAS_BASE)
777         port_sys = random() & ALIAS_PORT_MASK_EVEN;
778       else
779         port_sys = random() & ALIAS_PORT_MASK;
780 
781       port_sys += ALIAS_PORT_BASE;
782     }
783 
784 /* Port number search */
785     for (i = 0; i < max_trials; i++) {
786 
787       struct alias_link *search_result;
788 
789       for (j = 0; j < port_count; j++)
790         if (0 != (search_result = FindLinkIn(dst_addr, alias_addr,
791                                         dst_port, htons(port_sys + j),
792                                         link_type, 0)))
793 	  break;
794 
795       /* Found a good range, return base */
796       if (j == port_count)
797 	return (htons(port_sys));
798 
799       /* Find a new base to try */
800       if (align == FIND_EVEN_ALIAS_BASE)
801         port_sys = random() & ALIAS_PORT_MASK_EVEN;
802       else
803         port_sys = random() & ALIAS_PORT_MASK;
804 
805       port_sys += ALIAS_PORT_BASE;
806     }
807 
808 #ifdef DEBUG
809     fprintf(stderr, "PacketAlias/FindNewPortGroup(): ");
810     fprintf(stderr, "could not find free port(s)\n");
811 #endif
812 
813     return(0);
814 }
815 
816 static void
817 CleanupAliasData(void)
818 {
819     struct alias_link *link;
820     int i, icount;
821 
822     icount = 0;
823     for (i=0; i<LINK_TABLE_OUT_SIZE; i++)
824     {
825         link = LIST_FIRST(&linkTableOut[i]);
826         while (link != NULL)
827         {
828             struct alias_link *link_next;
829             link_next = LIST_NEXT(link, list_out);
830             icount++;
831             DeleteLink(link);
832             link = link_next;
833         }
834     }
835 
836     cleanupIndex =0;
837 }
838 
839 
840 static void
841 IncrementalCleanup(void)
842 {
843     int icount;
844     struct alias_link *link;
845 
846     icount = 0;
847     link = LIST_FIRST(&linkTableOut[cleanupIndex++]);
848     while (link != NULL)
849     {
850         int idelta;
851         struct alias_link *link_next;
852 
853         link_next = LIST_NEXT(link, list_out);
854         idelta = timeStamp - link->timestamp;
855         switch (link->link_type)
856         {
857             case LINK_TCP:
858                 if (idelta > link->expire_time)
859                 {
860                     struct tcp_dat *tcp_aux;
861 
862                     tcp_aux = link->data.tcp;
863                     if (tcp_aux->state.in  != ALIAS_TCP_STATE_CONNECTED
864                      || tcp_aux->state.out != ALIAS_TCP_STATE_CONNECTED)
865                     {
866                         DeleteLink(link);
867                         icount++;
868                     }
869                 }
870                 break;
871             default:
872                 if (idelta > link->expire_time)
873                 {
874                     DeleteLink(link);
875                     icount++;
876                 }
877                 break;
878         }
879         link = link_next;
880     }
881 
882     if (cleanupIndex == LINK_TABLE_OUT_SIZE)
883         cleanupIndex = 0;
884 }
885 
886 static void
887 DeleteLink(struct alias_link *link)
888 {
889 
890 /* Don't do anything if the link is marked permanent */
891     if (deleteAllLinks == 0 && link->flags & LINK_PERMANENT)
892         return;
893 
894 #ifndef NO_FW_PUNCH
895 /* Delete associated firewall hole, if any */
896     ClearFWHole(link);
897 #endif
898 
899 /* Free memory allocated for LSNAT server pool */
900     if (link->server != NULL) {
901 	struct server *head, *curr, *next;
902 
903 	head = curr = link->server;
904 	do {
905 	    next = curr->next;
906 	    free(curr);
907 	} while ((curr = next) != head);
908     }
909 
910 /* Adjust output table pointers */
911     LIST_REMOVE(link, list_out);
912 
913 /* Adjust input table pointers */
914     LIST_REMOVE(link, list_in);
915 
916 /* Close socket, if one has been allocated */
917     if (link->sockfd != -1)
918     {
919         sockCount--;
920         close(link->sockfd);
921     }
922 
923 /* Link-type dependent cleanup */
924     switch(link->link_type)
925     {
926         case LINK_ICMP:
927             icmpLinkCount--;
928             break;
929         case LINK_UDP:
930             udpLinkCount--;
931             break;
932         case LINK_TCP:
933             tcpLinkCount--;
934             free(link->data.tcp);
935             break;
936         case LINK_PPTP:
937             pptpLinkCount--;
938             break;
939         case LINK_FRAGMENT_ID:
940             fragmentIdLinkCount--;
941             break;
942         case LINK_FRAGMENT_PTR:
943             fragmentPtrLinkCount--;
944             if (link->data.frag_ptr != NULL)
945                 free(link->data.frag_ptr);
946             break;
947 	case LINK_ADDR:
948 	    break;
949         default:
950             protoLinkCount--;
951             break;
952     }
953 
954 /* Free memory */
955     free(link);
956 
957 /* Write statistics, if logging enabled */
958     if (packetAliasMode & PKT_ALIAS_LOG)
959     {
960         ShowAliasStats();
961     }
962 }
963 
964 
965 static struct alias_link *
966 AddLink(struct in_addr  src_addr,
967         struct in_addr  dst_addr,
968         struct in_addr  alias_addr,
969         u_short         src_port,
970         u_short         dst_port,
971         int             alias_port_param,  /* if less than zero, alias   */
972         int             link_type)         /* port will be automatically */
973 {                                          /* chosen. If greater than    */
974     u_int start_point;                     /* zero, equal to alias port  */
975     struct alias_link *link;
976 
977     link = malloc(sizeof(struct alias_link));
978     if (link != NULL)
979     {
980     /* Basic initialization */
981         link->src_addr          = src_addr;
982         link->dst_addr          = dst_addr;
983         link->alias_addr        = alias_addr;
984         link->proxy_addr.s_addr = INADDR_ANY;
985         link->src_port          = src_port;
986         link->dst_port          = dst_port;
987         link->proxy_port        = 0;
988         link->server            = NULL;
989         link->link_type         = link_type;
990         link->sockfd            = -1;
991         link->flags             = 0;
992         link->timestamp         = timeStamp;
993 
994     /* Expiration time */
995         switch (link_type)
996         {
997         case LINK_ICMP:
998             link->expire_time = ICMP_EXPIRE_TIME;
999             break;
1000         case LINK_UDP:
1001             link->expire_time = UDP_EXPIRE_TIME;
1002             break;
1003         case LINK_TCP:
1004             link->expire_time = TCP_EXPIRE_INITIAL;
1005             break;
1006         case LINK_PPTP:
1007             link->expire_time = PPTP_EXPIRE_TIME;
1008             break;
1009         case LINK_FRAGMENT_ID:
1010             link->expire_time = FRAGMENT_ID_EXPIRE_TIME;
1011             break;
1012         case LINK_FRAGMENT_PTR:
1013             link->expire_time = FRAGMENT_PTR_EXPIRE_TIME;
1014             break;
1015 	case LINK_ADDR:
1016 	    break;
1017         default:
1018             link->expire_time = PROTO_EXPIRE_TIME;
1019             break;
1020         }
1021 
1022     /* Determine alias flags */
1023         if (dst_addr.s_addr == INADDR_ANY)
1024             link->flags |= LINK_UNKNOWN_DEST_ADDR;
1025         if (dst_port == 0)
1026             link->flags |= LINK_UNKNOWN_DEST_PORT;
1027 
1028     /* Determine alias port */
1029         if (GetNewPort(link, alias_port_param) != 0)
1030         {
1031             free(link);
1032             return(NULL);
1033         }
1034 
1035     /* Link-type dependent initialization */
1036         switch(link_type)
1037         {
1038             struct tcp_dat  *aux_tcp;
1039 
1040             case LINK_ICMP:
1041                 icmpLinkCount++;
1042                 break;
1043             case LINK_UDP:
1044                 udpLinkCount++;
1045                 break;
1046             case LINK_TCP:
1047                 aux_tcp = malloc(sizeof(struct tcp_dat));
1048                 if (aux_tcp != NULL)
1049                 {
1050                     int i;
1051 
1052                     tcpLinkCount++;
1053                     aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED;
1054                     aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED;
1055                     aux_tcp->state.index = 0;
1056                     aux_tcp->state.ack_modified = 0;
1057                     for (i=0; i<N_LINK_TCP_DATA; i++)
1058                         aux_tcp->ack[i].active = 0;
1059                     aux_tcp->fwhole = -1;
1060                     link->data.tcp = aux_tcp;
1061                 }
1062                 else
1063                 {
1064 #ifdef DEBUG
1065                     fprintf(stderr, "PacketAlias/AddLink: ");
1066                     fprintf(stderr, " cannot allocate auxiliary TCP data\n");
1067 #endif
1068 		    free(link);
1069 		    return (NULL);
1070                 }
1071                 break;
1072             case LINK_PPTP:
1073                 pptpLinkCount++;
1074                 break;
1075             case LINK_FRAGMENT_ID:
1076                 fragmentIdLinkCount++;
1077                 break;
1078             case LINK_FRAGMENT_PTR:
1079                 fragmentPtrLinkCount++;
1080                 break;
1081 	    case LINK_ADDR:
1082 		break;
1083             default:
1084                 protoLinkCount++;
1085                 break;
1086         }
1087 
1088     /* Set up pointers for output lookup table */
1089         start_point = StartPointOut(src_addr, dst_addr,
1090                                     src_port, dst_port, link_type);
1091         LIST_INSERT_HEAD(&linkTableOut[start_point], link, list_out);
1092 
1093     /* Set up pointers for input lookup table */
1094         start_point = StartPointIn(alias_addr, link->alias_port, link_type);
1095         LIST_INSERT_HEAD(&linkTableIn[start_point], link, list_in);
1096     }
1097     else
1098     {
1099 #ifdef DEBUG
1100         fprintf(stderr, "PacketAlias/AddLink(): ");
1101         fprintf(stderr, "malloc() call failed.\n");
1102 #endif
1103     }
1104 
1105     if (packetAliasMode & PKT_ALIAS_LOG)
1106     {
1107         ShowAliasStats();
1108     }
1109 
1110     return(link);
1111 }
1112 
1113 static struct alias_link *
1114 ReLink(struct alias_link *old_link,
1115        struct in_addr  src_addr,
1116        struct in_addr  dst_addr,
1117        struct in_addr  alias_addr,
1118        u_short         src_port,
1119        u_short         dst_port,
1120        int             alias_port_param,   /* if less than zero, alias   */
1121        int             link_type)          /* port will be automatically */
1122 {                                          /* chosen. If greater than    */
1123     struct alias_link *new_link;           /* zero, equal to alias port  */
1124 
1125     new_link = AddLink(src_addr, dst_addr, alias_addr,
1126                        src_port, dst_port, alias_port_param,
1127                        link_type);
1128 #ifndef NO_FW_PUNCH
1129     if (new_link != NULL &&
1130         old_link->link_type == LINK_TCP &&
1131         old_link->data.tcp->fwhole > 0) {
1132       PunchFWHole(new_link);
1133     }
1134 #endif
1135     DeleteLink(old_link);
1136     return new_link;
1137 }
1138 
1139 static struct alias_link *
1140 _FindLinkOut(struct in_addr src_addr,
1141             struct in_addr dst_addr,
1142             u_short src_port,
1143             u_short dst_port,
1144             int link_type,
1145             int replace_partial_links)
1146 {
1147     u_int i;
1148     struct alias_link *link;
1149 
1150     i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type);
1151     LIST_FOREACH(link, &linkTableOut[i], list_out)
1152     {
1153         if (link->src_addr.s_addr == src_addr.s_addr
1154          && link->server          == NULL
1155          && link->dst_addr.s_addr == dst_addr.s_addr
1156          && link->dst_port        == dst_port
1157          && link->src_port        == src_port
1158          && link->link_type       == link_type)
1159         {
1160             link->timestamp = timeStamp;
1161             break;
1162         }
1163     }
1164 
1165 /* Search for partially specified links. */
1166     if (link == NULL && replace_partial_links)
1167     {
1168         if (dst_port != 0 && dst_addr.s_addr != INADDR_ANY)
1169         {
1170             link = _FindLinkOut(src_addr, dst_addr, src_port, 0,
1171                                 link_type, 0);
1172             if (link == NULL)
1173                 link = _FindLinkOut(src_addr, nullAddress, src_port,
1174                                     dst_port, link_type, 0);
1175         }
1176         if (link == NULL &&
1177            (dst_port != 0 || dst_addr.s_addr != INADDR_ANY))
1178         {
1179             link = _FindLinkOut(src_addr, nullAddress, src_port, 0,
1180                                 link_type, 0);
1181         }
1182         if (link != NULL)
1183         {
1184             link = ReLink(link,
1185                           src_addr, dst_addr, link->alias_addr,
1186                           src_port, dst_port, link->alias_port,
1187                           link_type);
1188         }
1189     }
1190 
1191     return(link);
1192 }
1193 
1194 static struct alias_link *
1195 FindLinkOut(struct in_addr src_addr,
1196             struct in_addr dst_addr,
1197             u_short src_port,
1198             u_short dst_port,
1199             int link_type,
1200             int replace_partial_links)
1201 {
1202     struct alias_link *link;
1203 
1204     link = _FindLinkOut(src_addr, dst_addr, src_port, dst_port,
1205                         link_type, replace_partial_links);
1206 
1207     if (link == NULL)
1208     {
1209     /* The following allows permanent links to be
1210        specified as using the default source address
1211        (i.e. device interface address) without knowing
1212        in advance what that address is. */
1213         if (aliasAddress.s_addr != 0 &&
1214             src_addr.s_addr == aliasAddress.s_addr)
1215         {
1216             link = _FindLinkOut(nullAddress, dst_addr, src_port, dst_port,
1217                                link_type, replace_partial_links);
1218         }
1219     }
1220 
1221     return(link);
1222 }
1223 
1224 
1225 static struct alias_link *
1226 _FindLinkIn(struct in_addr dst_addr,
1227            struct in_addr  alias_addr,
1228            u_short         dst_port,
1229            u_short         alias_port,
1230            int             link_type,
1231            int             replace_partial_links)
1232 {
1233     int flags_in;
1234     u_int start_point;
1235     struct alias_link *link;
1236     struct alias_link *link_fully_specified;
1237     struct alias_link *link_unknown_all;
1238     struct alias_link *link_unknown_dst_addr;
1239     struct alias_link *link_unknown_dst_port;
1240 
1241 /* Initialize pointers */
1242     link_fully_specified  = NULL;
1243     link_unknown_all      = NULL;
1244     link_unknown_dst_addr = NULL;
1245     link_unknown_dst_port = NULL;
1246 
1247 /* If either the dest addr or port is unknown, the search
1248    loop will have to know about this. */
1249 
1250     flags_in = 0;
1251     if (dst_addr.s_addr == INADDR_ANY)
1252         flags_in |= LINK_UNKNOWN_DEST_ADDR;
1253     if (dst_port == 0)
1254         flags_in |= LINK_UNKNOWN_DEST_PORT;
1255 
1256 /* Search loop */
1257     start_point = StartPointIn(alias_addr, alias_port, link_type);
1258     LIST_FOREACH(link, &linkTableIn[start_point], list_in)
1259     {
1260         int flags;
1261 
1262         flags = flags_in | link->flags;
1263         if (!(flags & LINK_PARTIALLY_SPECIFIED))
1264         {
1265             if (link->alias_addr.s_addr == alias_addr.s_addr
1266              && link->alias_port        == alias_port
1267              && link->dst_addr.s_addr   == dst_addr.s_addr
1268              && link->dst_port          == dst_port
1269              && link->link_type         == link_type)
1270             {
1271                 link_fully_specified = link;
1272                 break;
1273             }
1274         }
1275         else if ((flags & LINK_UNKNOWN_DEST_ADDR)
1276               && (flags & LINK_UNKNOWN_DEST_PORT))
1277         {
1278             if (link->alias_addr.s_addr == alias_addr.s_addr
1279              && link->alias_port        == alias_port
1280              && link->link_type         == link_type)
1281             {
1282                 if (link_unknown_all == NULL)
1283                     link_unknown_all = link;
1284             }
1285         }
1286         else if (flags & LINK_UNKNOWN_DEST_ADDR)
1287         {
1288             if (link->alias_addr.s_addr == alias_addr.s_addr
1289              && link->alias_port        == alias_port
1290              && link->link_type         == link_type
1291              && link->dst_port          == dst_port)
1292             {
1293                 if (link_unknown_dst_addr == NULL)
1294                     link_unknown_dst_addr = link;
1295             }
1296         }
1297         else if (flags & LINK_UNKNOWN_DEST_PORT)
1298         {
1299             if (link->alias_addr.s_addr == alias_addr.s_addr
1300              && link->alias_port        == alias_port
1301              && link->link_type         == link_type
1302              && link->dst_addr.s_addr   == dst_addr.s_addr)
1303             {
1304                 if (link_unknown_dst_port == NULL)
1305                     link_unknown_dst_port = link;
1306             }
1307         }
1308     }
1309 
1310 
1311 
1312     if (link_fully_specified != NULL)
1313     {
1314         link_fully_specified->timestamp = timeStamp;
1315         link = link_fully_specified;
1316     }
1317     else if (link_unknown_dst_port != NULL)
1318 	link = link_unknown_dst_port;
1319     else if (link_unknown_dst_addr != NULL)
1320 	link = link_unknown_dst_addr;
1321     else if (link_unknown_all != NULL)
1322 	link = link_unknown_all;
1323     else
1324         return (NULL);
1325 
1326     if (replace_partial_links &&
1327 	(link->flags & LINK_PARTIALLY_SPECIFIED || link->server != NULL))
1328     {
1329 	struct in_addr src_addr;
1330 	u_short src_port;
1331 
1332 	if (link->server != NULL) {		/* LSNAT link */
1333 	    src_addr = link->server->addr;
1334 	    src_port = link->server->port;
1335 	    link->server = link->server->next;
1336 	} else {
1337 	    src_addr = link->src_addr;
1338 	    src_port = link->src_port;
1339 	}
1340 
1341 	link = ReLink(link,
1342 		      src_addr, dst_addr, alias_addr,
1343 		      src_port, dst_port, alias_port,
1344 		      link_type);
1345     }
1346 
1347     return (link);
1348 }
1349 
1350 static struct alias_link *
1351 FindLinkIn(struct in_addr dst_addr,
1352            struct in_addr alias_addr,
1353            u_short dst_port,
1354            u_short alias_port,
1355            int link_type,
1356            int replace_partial_links)
1357 {
1358     struct alias_link *link;
1359 
1360     link = _FindLinkIn(dst_addr, alias_addr, dst_port, alias_port,
1361                        link_type, replace_partial_links);
1362 
1363     if (link == NULL)
1364     {
1365     /* The following allows permanent links to be
1366        specified as using the default aliasing address
1367        (i.e. device interface address) without knowing
1368        in advance what that address is. */
1369         if (aliasAddress.s_addr != 0 &&
1370             alias_addr.s_addr == aliasAddress.s_addr)
1371         {
1372             link = _FindLinkIn(dst_addr, nullAddress, dst_port, alias_port,
1373                                link_type, replace_partial_links);
1374         }
1375     }
1376 
1377     return(link);
1378 }
1379 
1380 
1381 
1382 
1383 /* External routines for finding/adding links
1384 
1385 -- "external" means outside alias_db.c, but within alias*.c --
1386 
1387     FindIcmpIn(), FindIcmpOut()
1388     FindFragmentIn1(), FindFragmentIn2()
1389     AddFragmentPtrLink(), FindFragmentPtr()
1390     FindProtoIn(), FindProtoOut()
1391     FindUdpTcpIn(), FindUdpTcpOut()
1392     FindPptpIn(), FindPptpOut()
1393     FindOriginalAddress(), FindAliasAddress()
1394 
1395 (prototypes in alias_local.h)
1396 */
1397 
1398 
1399 struct alias_link *
1400 FindIcmpIn(struct in_addr dst_addr,
1401            struct in_addr alias_addr,
1402            u_short id_alias)
1403 {
1404     struct alias_link *link;
1405 
1406     link = FindLinkIn(dst_addr, alias_addr,
1407                       NO_DEST_PORT, id_alias,
1408                       LINK_ICMP, 0);
1409     if (link == NULL && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
1410     {
1411         struct in_addr target_addr;
1412 
1413         target_addr = FindOriginalAddress(alias_addr);
1414         link = AddLink(target_addr, dst_addr, alias_addr,
1415                        id_alias, NO_DEST_PORT, id_alias,
1416                        LINK_ICMP);
1417     }
1418 
1419     return (link);
1420 }
1421 
1422 
1423 struct alias_link *
1424 FindIcmpOut(struct in_addr src_addr,
1425             struct in_addr dst_addr,
1426             u_short id)
1427 {
1428     struct alias_link * link;
1429 
1430     link = FindLinkOut(src_addr, dst_addr,
1431                        id, NO_DEST_PORT,
1432                        LINK_ICMP, 0);
1433     if (link == NULL)
1434     {
1435         struct in_addr alias_addr;
1436 
1437         alias_addr = FindAliasAddress(src_addr);
1438         link = AddLink(src_addr, dst_addr, alias_addr,
1439                        id, NO_DEST_PORT, GET_ALIAS_ID,
1440                        LINK_ICMP);
1441     }
1442 
1443     return(link);
1444 }
1445 
1446 
1447 struct alias_link *
1448 FindFragmentIn1(struct in_addr dst_addr,
1449                 struct in_addr alias_addr,
1450                 u_short ip_id)
1451 {
1452     struct alias_link *link;
1453 
1454     link = FindLinkIn(dst_addr, alias_addr,
1455                       NO_DEST_PORT, ip_id,
1456                       LINK_FRAGMENT_ID, 0);
1457 
1458     if (link == NULL)
1459     {
1460         link = AddLink(nullAddress, dst_addr, alias_addr,
1461                        NO_SRC_PORT, NO_DEST_PORT, ip_id,
1462                        LINK_FRAGMENT_ID);
1463     }
1464 
1465     return(link);
1466 }
1467 
1468 
1469 struct alias_link *
1470 FindFragmentIn2(struct in_addr dst_addr,   /* Doesn't add a link if one */
1471                 struct in_addr alias_addr, /*   is not found.           */
1472                 u_short ip_id)
1473 {
1474     return FindLinkIn(dst_addr, alias_addr,
1475                       NO_DEST_PORT, ip_id,
1476                       LINK_FRAGMENT_ID, 0);
1477 }
1478 
1479 
1480 struct alias_link *
1481 AddFragmentPtrLink(struct in_addr dst_addr,
1482                    u_short ip_id)
1483 {
1484     return AddLink(nullAddress, dst_addr, nullAddress,
1485                    NO_SRC_PORT, NO_DEST_PORT, ip_id,
1486                    LINK_FRAGMENT_PTR);
1487 }
1488 
1489 
1490 struct alias_link *
1491 FindFragmentPtr(struct in_addr dst_addr,
1492                 u_short ip_id)
1493 {
1494     return FindLinkIn(dst_addr, nullAddress,
1495                       NO_DEST_PORT, ip_id,
1496                       LINK_FRAGMENT_PTR, 0);
1497 }
1498 
1499 
1500 struct alias_link *
1501 FindProtoIn(struct in_addr dst_addr,
1502             struct in_addr alias_addr,
1503 	    u_char proto)
1504 {
1505     struct alias_link *link;
1506 
1507     link = FindLinkIn(dst_addr, alias_addr,
1508                       NO_DEST_PORT, 0,
1509                       proto, 1);
1510 
1511     if (link == NULL && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
1512     {
1513         struct in_addr target_addr;
1514 
1515         target_addr = FindOriginalAddress(alias_addr);
1516         link = AddLink(target_addr, dst_addr, alias_addr,
1517                        NO_SRC_PORT, NO_DEST_PORT, 0,
1518                        proto);
1519     }
1520 
1521     return (link);
1522 }
1523 
1524 
1525 struct alias_link *
1526 FindProtoOut(struct in_addr src_addr,
1527              struct in_addr dst_addr,
1528              u_char proto)
1529 {
1530     struct alias_link *link;
1531 
1532     link = FindLinkOut(src_addr, dst_addr,
1533                        NO_SRC_PORT, NO_DEST_PORT,
1534                        proto, 1);
1535 
1536     if (link == NULL)
1537     {
1538         struct in_addr alias_addr;
1539 
1540         alias_addr = FindAliasAddress(src_addr);
1541         link = AddLink(src_addr, dst_addr, alias_addr,
1542                        NO_SRC_PORT, NO_DEST_PORT, 0,
1543                        proto);
1544     }
1545 
1546     return (link);
1547 }
1548 
1549 
1550 struct alias_link *
1551 FindUdpTcpIn(struct in_addr dst_addr,
1552              struct in_addr alias_addr,
1553              u_short        dst_port,
1554              u_short        alias_port,
1555              u_char         proto)
1556 {
1557     int link_type;
1558     struct alias_link *link;
1559 
1560     switch (proto)
1561     {
1562     case IPPROTO_UDP:
1563         link_type = LINK_UDP;
1564         break;
1565     case IPPROTO_TCP:
1566         link_type = LINK_TCP;
1567         break;
1568     default:
1569         return NULL;
1570         break;
1571     }
1572 
1573     link = FindLinkIn(dst_addr, alias_addr,
1574                       dst_port, alias_port,
1575                       link_type, 1);
1576 
1577     if (!(packetAliasMode & PKT_ALIAS_DENY_INCOMING)
1578      && !(packetAliasMode & PKT_ALIAS_PROXY_ONLY)
1579      && link == NULL)
1580     {
1581         struct in_addr target_addr;
1582 
1583         target_addr = FindOriginalAddress(alias_addr);
1584         link = AddLink(target_addr, dst_addr, alias_addr,
1585                        alias_port, dst_port, alias_port,
1586                        link_type);
1587     }
1588 
1589     return(link);
1590 }
1591 
1592 
1593 struct alias_link *
1594 FindUdpTcpOut(struct in_addr  src_addr,
1595               struct in_addr  dst_addr,
1596               u_short         src_port,
1597               u_short         dst_port,
1598               u_char          proto)
1599 {
1600     int link_type;
1601     struct alias_link *link;
1602 
1603     switch (proto)
1604     {
1605     case IPPROTO_UDP:
1606         link_type = LINK_UDP;
1607         break;
1608     case IPPROTO_TCP:
1609         link_type = LINK_TCP;
1610         break;
1611     default:
1612         return NULL;
1613         break;
1614     }
1615 
1616     link = FindLinkOut(src_addr, dst_addr, src_port, dst_port, link_type, 1);
1617 
1618     if (link == NULL)
1619     {
1620         struct in_addr alias_addr;
1621 
1622         alias_addr = FindAliasAddress(src_addr);
1623         link = AddLink(src_addr, dst_addr, alias_addr,
1624                        src_port, dst_port, GET_ALIAS_PORT,
1625                        link_type);
1626     }
1627 
1628     return(link);
1629 }
1630 
1631 
1632 struct alias_link *
1633 FindPptpIn(struct in_addr dst_addr,
1634           struct in_addr alias_addr,
1635           u_short call_id)
1636 {
1637     struct alias_link *link;
1638 
1639     link = FindLinkIn(dst_addr, alias_addr,
1640                       NO_DEST_PORT, call_id,
1641                       LINK_PPTP, 1);
1642 
1643     if (link == NULL && !(packetAliasMode & PKT_ALIAS_DENY_INCOMING))
1644     {
1645         struct in_addr target_addr;
1646 
1647         target_addr = FindOriginalAddress(alias_addr);
1648         link = AddLink(target_addr, dst_addr, alias_addr,
1649                        call_id, NO_DEST_PORT, call_id,
1650                        LINK_PPTP);
1651     }
1652 
1653     return(link);
1654 }
1655 
1656 
1657 struct alias_link *
1658 FindPptpOut(struct in_addr  src_addr,
1659             struct in_addr  dst_addr,
1660             u_short         call_id)
1661 {
1662     struct alias_link *link;
1663 
1664     link = FindLinkOut(src_addr, dst_addr,
1665                        call_id, NO_DEST_PORT,
1666                        LINK_PPTP, 1);
1667 
1668     if (link == NULL)
1669     {
1670         struct in_addr alias_addr;
1671 
1672         alias_addr = FindAliasAddress(src_addr);
1673         link = AddLink(src_addr, dst_addr, alias_addr,
1674                        call_id, NO_DEST_PORT, GET_ALIAS_PORT,
1675                        LINK_PPTP);
1676     }
1677 
1678     return(link);
1679 }
1680 
1681 
1682 struct alias_link *
1683 QueryUdpTcpIn(struct in_addr dst_addr,
1684               struct in_addr alias_addr,
1685               u_short        dst_port,
1686               u_short        alias_port,
1687               u_char         proto)
1688 {
1689     int link_type;
1690     struct alias_link *link;
1691 
1692     switch (proto)
1693     {
1694     case IPPROTO_UDP:
1695         link_type = LINK_UDP;
1696         break;
1697     case IPPROTO_TCP:
1698         link_type = LINK_TCP;
1699         break;
1700     default:
1701         return NULL;
1702         break;
1703     }
1704 
1705     link = FindLinkIn(dst_addr, alias_addr,
1706                       dst_port, alias_port,
1707                       link_type, 0);
1708 
1709     return(link);
1710 }
1711 
1712 
1713 struct alias_link *
1714 QueryUdpTcpOut(struct in_addr  src_addr,
1715                struct in_addr  dst_addr,
1716                u_short         src_port,
1717                u_short         dst_port,
1718                u_char          proto)
1719 {
1720     int link_type;
1721     struct alias_link *link;
1722 
1723     switch (proto)
1724     {
1725     case IPPROTO_UDP:
1726         link_type = LINK_UDP;
1727         break;
1728     case IPPROTO_TCP:
1729         link_type = LINK_TCP;
1730         break;
1731     default:
1732         return NULL;
1733         break;
1734     }
1735 
1736     link = FindLinkOut(src_addr, dst_addr,
1737                        src_port, dst_port,
1738 		       link_type, 0);
1739 
1740     return(link);
1741 }
1742 
1743 
1744 struct alias_link *
1745 FindRtspOut(struct in_addr  src_addr,
1746             struct in_addr  dst_addr,
1747             u_short         src_port,
1748             u_short         alias_port,
1749             u_char          proto)
1750 {
1751     int link_type;
1752     struct alias_link *link;
1753 
1754     switch (proto)
1755     {
1756     case IPPROTO_UDP:
1757         link_type = LINK_UDP;
1758         break;
1759     case IPPROTO_TCP:
1760         link_type = LINK_TCP;
1761         break;
1762     default:
1763         return NULL;
1764         break;
1765     }
1766 
1767     link = FindLinkOut(src_addr, dst_addr, src_port, 0, link_type, 1);
1768 
1769     if (link == NULL)
1770     {
1771         struct in_addr alias_addr;
1772 
1773         alias_addr = FindAliasAddress(src_addr);
1774         link = AddLink(src_addr, dst_addr, alias_addr,
1775                        src_port, 0, alias_port,
1776                        link_type);
1777     }
1778 
1779     return(link);
1780 }
1781 
1782 
1783 struct in_addr
1784 FindOriginalAddress(struct in_addr alias_addr)
1785 {
1786     struct alias_link *link;
1787 
1788     link = FindLinkIn(nullAddress, alias_addr,
1789                       0, 0, LINK_ADDR, 0);
1790     if (link == NULL)
1791     {
1792         newDefaultLink = 1;
1793         if (targetAddress.s_addr == INADDR_ANY)
1794             return alias_addr;
1795         else if (targetAddress.s_addr == INADDR_NONE)
1796             return aliasAddress;
1797         else
1798             return targetAddress;
1799     }
1800     else
1801     {
1802 	if (link->server != NULL) {		/* LSNAT link */
1803 	    struct in_addr src_addr;
1804 
1805 	    src_addr = link->server->addr;
1806 	    link->server = link->server->next;
1807 	    return (src_addr);
1808         } else if (link->src_addr.s_addr == INADDR_ANY)
1809             return aliasAddress;
1810         else
1811             return link->src_addr;
1812     }
1813 }
1814 
1815 
1816 struct in_addr
1817 FindAliasAddress(struct in_addr original_addr)
1818 {
1819     struct alias_link *link;
1820 
1821     link = FindLinkOut(original_addr, nullAddress,
1822                        0, 0, LINK_ADDR, 0);
1823     if (link == NULL)
1824     {
1825         return aliasAddress;
1826     }
1827     else
1828     {
1829         if (link->alias_addr.s_addr == INADDR_ANY)
1830             return aliasAddress;
1831         else
1832             return link->alias_addr;
1833     }
1834 }
1835 
1836 
1837 /* External routines for getting or changing link data
1838    (external to alias_db.c, but internal to alias*.c)
1839 
1840     SetFragmentData(), GetFragmentData()
1841     SetFragmentPtr(), GetFragmentPtr()
1842     SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut()
1843     GetOriginalAddress(), GetDestAddress(), GetAliasAddress()
1844     GetOriginalPort(), GetAliasPort()
1845     SetAckModified(), GetAckModified()
1846     GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq()
1847     SetLastLineCrlfTermed(), GetLastLineCrlfTermed()
1848 */
1849 
1850 
1851 void
1852 SetFragmentAddr(struct alias_link *link, struct in_addr src_addr)
1853 {
1854     link->data.frag_addr = src_addr;
1855 }
1856 
1857 
1858 void
1859 GetFragmentAddr(struct alias_link *link, struct in_addr *src_addr)
1860 {
1861     *src_addr = link->data.frag_addr;
1862 }
1863 
1864 
1865 void
1866 SetFragmentPtr(struct alias_link *link, char *fptr)
1867 {
1868     link->data.frag_ptr = fptr;
1869 }
1870 
1871 
1872 void
1873 GetFragmentPtr(struct alias_link *link, char **fptr)
1874 {
1875    *fptr = link->data.frag_ptr;
1876 }
1877 
1878 
1879 void
1880 SetStateIn(struct alias_link *link, int state)
1881 {
1882     /* TCP input state */
1883     switch (state) {
1884     case ALIAS_TCP_STATE_DISCONNECTED:
1885         if (link->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED)
1886             link->expire_time = TCP_EXPIRE_DEAD;
1887         else
1888             link->expire_time = TCP_EXPIRE_SINGLEDEAD;
1889         break;
1890     case ALIAS_TCP_STATE_CONNECTED:
1891         if (link->data.tcp->state.out == ALIAS_TCP_STATE_CONNECTED)
1892             link->expire_time = TCP_EXPIRE_CONNECTED;
1893         break;
1894     default:
1895         abort();
1896     }
1897     link->data.tcp->state.in = state;
1898 }
1899 
1900 
1901 void
1902 SetStateOut(struct alias_link *link, int state)
1903 {
1904     /* TCP output state */
1905     switch (state) {
1906     case ALIAS_TCP_STATE_DISCONNECTED:
1907         if (link->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED)
1908             link->expire_time = TCP_EXPIRE_DEAD;
1909         else
1910             link->expire_time = TCP_EXPIRE_SINGLEDEAD;
1911         break;
1912     case ALIAS_TCP_STATE_CONNECTED:
1913         if (link->data.tcp->state.in == ALIAS_TCP_STATE_CONNECTED)
1914             link->expire_time = TCP_EXPIRE_CONNECTED;
1915         break;
1916     default:
1917         abort();
1918     }
1919     link->data.tcp->state.out = state;
1920 }
1921 
1922 
1923 int
1924 GetStateIn(struct alias_link *link)
1925 {
1926     /* TCP input state */
1927     return link->data.tcp->state.in;
1928 }
1929 
1930 
1931 int
1932 GetStateOut(struct alias_link *link)
1933 {
1934     /* TCP output state */
1935     return link->data.tcp->state.out;
1936 }
1937 
1938 
1939 struct in_addr
1940 GetOriginalAddress(struct alias_link *link)
1941 {
1942     if (link->src_addr.s_addr == INADDR_ANY)
1943         return aliasAddress;
1944     else
1945         return(link->src_addr);
1946 }
1947 
1948 
1949 struct in_addr
1950 GetDestAddress(struct alias_link *link)
1951 {
1952     return(link->dst_addr);
1953 }
1954 
1955 
1956 struct in_addr
1957 GetAliasAddress(struct alias_link *link)
1958 {
1959     if (link->alias_addr.s_addr == INADDR_ANY)
1960         return aliasAddress;
1961     else
1962         return link->alias_addr;
1963 }
1964 
1965 
1966 struct in_addr
1967 GetDefaultAliasAddress()
1968 {
1969     return aliasAddress;
1970 }
1971 
1972 
1973 void
1974 SetDefaultAliasAddress(struct in_addr alias_addr)
1975 {
1976     aliasAddress = alias_addr;
1977 }
1978 
1979 
1980 u_short
1981 GetOriginalPort(struct alias_link *link)
1982 {
1983     return(link->src_port);
1984 }
1985 
1986 
1987 u_short
1988 GetAliasPort(struct alias_link *link)
1989 {
1990     return(link->alias_port);
1991 }
1992 
1993 #ifndef NO_FW_PUNCH
1994 static u_short
1995 GetDestPort(struct alias_link *link)
1996 {
1997     return(link->dst_port);
1998 }
1999 #endif
2000 
2001 void
2002 SetAckModified(struct alias_link *link)
2003 {
2004 /* Indicate that ACK numbers have been modified in a TCP connection */
2005     link->data.tcp->state.ack_modified = 1;
2006 }
2007 
2008 
2009 struct in_addr
2010 GetProxyAddress(struct alias_link *link)
2011 {
2012     return link->proxy_addr;
2013 }
2014 
2015 
2016 void
2017 SetProxyAddress(struct alias_link *link, struct in_addr addr)
2018 {
2019     link->proxy_addr = addr;
2020 }
2021 
2022 
2023 u_short
2024 GetProxyPort(struct alias_link *link)
2025 {
2026     return link->proxy_port;
2027 }
2028 
2029 
2030 void
2031 SetProxyPort(struct alias_link *link, u_short port)
2032 {
2033     link->proxy_port = port;
2034 }
2035 
2036 
2037 int
2038 GetAckModified(struct alias_link *link)
2039 {
2040 /* See if ACK numbers have been modified */
2041     return link->data.tcp->state.ack_modified;
2042 }
2043 
2044 
2045 int
2046 GetDeltaAckIn(struct ip *pip, struct alias_link *link)
2047 {
2048 /*
2049 Find out how much the ACK number has been altered for an incoming
2050 TCP packet.  To do this, a circular list of ACK numbers where the TCP
2051 packet size was altered is searched.
2052 */
2053 
2054     int i;
2055     struct tcphdr *tc;
2056     int delta, ack_diff_min;
2057     u_long ack;
2058 
2059     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
2060     ack      = tc->th_ack;
2061 
2062     delta = 0;
2063     ack_diff_min = -1;
2064     for (i=0; i<N_LINK_TCP_DATA; i++)
2065     {
2066         struct ack_data_record x;
2067 
2068         x = link->data.tcp->ack[i];
2069         if (x.active == 1)
2070         {
2071             int ack_diff;
2072 
2073             ack_diff = SeqDiff(x.ack_new, ack);
2074             if (ack_diff >= 0)
2075             {
2076                 if (ack_diff_min >= 0)
2077                 {
2078                     if (ack_diff < ack_diff_min)
2079                     {
2080                         delta = x.delta;
2081                         ack_diff_min = ack_diff;
2082                     }
2083                 }
2084                 else
2085                 {
2086                     delta = x.delta;
2087                     ack_diff_min = ack_diff;
2088                 }
2089             }
2090         }
2091     }
2092     return (delta);
2093 }
2094 
2095 
2096 int
2097 GetDeltaSeqOut(struct ip *pip, struct alias_link *link)
2098 {
2099 /*
2100 Find out how much the sequence number has been altered for an outgoing
2101 TCP packet.  To do this, a circular list of ACK numbers where the TCP
2102 packet size was altered is searched.
2103 */
2104 
2105     int i;
2106     struct tcphdr *tc;
2107     int delta, seq_diff_min;
2108     u_long seq;
2109 
2110     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
2111     seq = tc->th_seq;
2112 
2113     delta = 0;
2114     seq_diff_min = -1;
2115     for (i=0; i<N_LINK_TCP_DATA; i++)
2116     {
2117         struct ack_data_record x;
2118 
2119         x = link->data.tcp->ack[i];
2120         if (x.active == 1)
2121         {
2122             int seq_diff;
2123 
2124             seq_diff = SeqDiff(x.ack_old, seq);
2125             if (seq_diff >= 0)
2126             {
2127                 if (seq_diff_min >= 0)
2128                 {
2129                     if (seq_diff < seq_diff_min)
2130                     {
2131                         delta = x.delta;
2132                         seq_diff_min = seq_diff;
2133                     }
2134                 }
2135                 else
2136                 {
2137                     delta = x.delta;
2138                     seq_diff_min = seq_diff;
2139                 }
2140             }
2141         }
2142     }
2143     return (delta);
2144 }
2145 
2146 
2147 void
2148 AddSeq(struct ip *pip, struct alias_link *link, int delta)
2149 {
2150 /*
2151 When a TCP packet has been altered in length, save this
2152 information in a circular list.  If enough packets have
2153 been altered, then this list will begin to overwrite itself.
2154 */
2155 
2156     struct tcphdr *tc;
2157     struct ack_data_record x;
2158     int hlen, tlen, dlen;
2159     int i;
2160 
2161     tc = (struct tcphdr *) ((char *) pip + (pip->ip_hl << 2));
2162 
2163     hlen = (pip->ip_hl + tc->th_off) << 2;
2164     tlen = ntohs(pip->ip_len);
2165     dlen = tlen - hlen;
2166 
2167     x.ack_old = htonl(ntohl(tc->th_seq) + dlen);
2168     x.ack_new = htonl(ntohl(tc->th_seq) + dlen + delta);
2169     x.delta = delta;
2170     x.active = 1;
2171 
2172     i = link->data.tcp->state.index;
2173     link->data.tcp->ack[i] = x;
2174 
2175     i++;
2176     if (i == N_LINK_TCP_DATA)
2177         link->data.tcp->state.index = 0;
2178     else
2179         link->data.tcp->state.index = i;
2180 }
2181 
2182 void
2183 SetExpire(struct alias_link *link, int expire)
2184 {
2185     if (expire == 0)
2186     {
2187         link->flags &= ~LINK_PERMANENT;
2188         DeleteLink(link);
2189     }
2190     else if (expire == -1)
2191     {
2192         link->flags |= LINK_PERMANENT;
2193     }
2194     else if (expire > 0)
2195     {
2196         link->expire_time = expire;
2197     }
2198     else
2199     {
2200 #ifdef DEBUG
2201         fprintf(stderr, "PacketAlias/SetExpire(): ");
2202         fprintf(stderr, "error in expire parameter\n");
2203 #endif
2204     }
2205 }
2206 
2207 void
2208 ClearCheckNewLink(void)
2209 {
2210     newDefaultLink = 0;
2211 }
2212 
2213 void
2214 SetLastLineCrlfTermed(struct alias_link *link, int yes)
2215 {
2216 
2217     if (yes)
2218 	link->flags |= LINK_LAST_LINE_CRLF_TERMED;
2219     else
2220 	link->flags &= ~LINK_LAST_LINE_CRLF_TERMED;
2221 }
2222 
2223 int
2224 GetLastLineCrlfTermed(struct alias_link *link)
2225 {
2226 
2227     return (link->flags & LINK_LAST_LINE_CRLF_TERMED);
2228 }
2229 
2230 
2231 /* Miscellaneous Functions
2232 
2233     HouseKeeping()
2234     InitPacketAliasLog()
2235     UninitPacketAliasLog()
2236 */
2237 
2238 /*
2239     Whenever an outgoing or incoming packet is handled, HouseKeeping()
2240     is called to find and remove timed-out aliasing links.  Logic exists
2241     to sweep through the entire table and linked list structure
2242     every 60 seconds.
2243 
2244     (prototype in alias_local.h)
2245 */
2246 
2247 void
2248 HouseKeeping(void)
2249 {
2250     int i, n, n100;
2251     struct timeval tv;
2252     struct timezone tz;
2253 
2254     /*
2255      * Save system time (seconds) in global variable timeStamp for
2256      * use by other functions. This is done so as not to unnecessarily
2257      * waste timeline by making system calls.
2258      */
2259     gettimeofday(&tv, &tz);
2260     timeStamp = tv.tv_sec;
2261 
2262     /* Compute number of spokes (output table link chains) to cover */
2263     n100  = LINK_TABLE_OUT_SIZE * 100 + houseKeepingResidual;
2264     n100 *= timeStamp - lastCleanupTime;
2265     n100 /= ALIAS_CLEANUP_INTERVAL_SECS;
2266 
2267     n = n100/100;
2268 
2269     /* Handle different cases */
2270     if (n > ALIAS_CLEANUP_MAX_SPOKES)
2271     {
2272         n = ALIAS_CLEANUP_MAX_SPOKES;
2273         lastCleanupTime = timeStamp;
2274         houseKeepingResidual = 0;
2275 
2276         for (i=0; i<n; i++)
2277             IncrementalCleanup();
2278     }
2279     else if (n > 0)
2280     {
2281         lastCleanupTime = timeStamp;
2282         houseKeepingResidual = n100 - 100*n;
2283 
2284         for (i=0; i<n; i++)
2285             IncrementalCleanup();
2286     }
2287     else if (n < 0)
2288     {
2289 #ifdef DEBUG
2290         fprintf(stderr, "PacketAlias/HouseKeeping(): ");
2291         fprintf(stderr, "something unexpected in time values\n");
2292 #endif
2293         lastCleanupTime = timeStamp;
2294         houseKeepingResidual = 0;
2295     }
2296 }
2297 
2298 
2299 /* Init the log file and enable logging */
2300 static void
2301 InitPacketAliasLog(void)
2302 {
2303    if ((~packetAliasMode & PKT_ALIAS_LOG)
2304     && (monitorFile = fopen("/var/log/alias.log", "w")))
2305    {
2306       packetAliasMode |= PKT_ALIAS_LOG;
2307       fprintf(monitorFile,
2308       "PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n");
2309    }
2310 }
2311 
2312 
2313 /* Close the log-file and disable logging. */
2314 static void
2315 UninitPacketAliasLog(void)
2316 {
2317     if (monitorFile) {
2318         fclose(monitorFile);
2319         monitorFile = NULL;
2320     }
2321     packetAliasMode &= ~PKT_ALIAS_LOG;
2322 }
2323 
2324 
2325 
2326 
2327 
2328 
2329 /* Outside world interfaces
2330 
2331 -- "outside world" means other than alias*.c routines --
2332 
2333     PacketAliasRedirectPort()
2334     PacketAliasAddServer()
2335     PacketAliasRedirectProto()
2336     PacketAliasRedirectAddr()
2337     PacketAliasRedirectDelete()
2338     PacketAliasSetAddress()
2339     PacketAliasInit()
2340     PacketAliasUninit()
2341     PacketAliasSetMode()
2342 
2343 (prototypes in alias.h)
2344 */
2345 
2346 /* Redirection from a specific public addr:port to a
2347    private addr:port */
2348 struct alias_link *
2349 PacketAliasRedirectPort(struct in_addr src_addr,   u_short src_port,
2350                         struct in_addr dst_addr,   u_short dst_port,
2351                         struct in_addr alias_addr, u_short alias_port,
2352                         u_char proto)
2353 {
2354     int link_type;
2355     struct alias_link *link;
2356 
2357     switch(proto)
2358     {
2359     case IPPROTO_UDP:
2360         link_type = LINK_UDP;
2361         break;
2362     case IPPROTO_TCP:
2363         link_type = LINK_TCP;
2364         break;
2365     default:
2366 #ifdef DEBUG
2367         fprintf(stderr, "PacketAliasRedirectPort(): ");
2368         fprintf(stderr, "only TCP and UDP protocols allowed\n");
2369 #endif
2370         return NULL;
2371     }
2372 
2373     link = AddLink(src_addr, dst_addr, alias_addr,
2374                    src_port, dst_port, alias_port,
2375                    link_type);
2376 
2377     if (link != NULL)
2378     {
2379         link->flags |= LINK_PERMANENT;
2380     }
2381 #ifdef DEBUG
2382     else
2383     {
2384         fprintf(stderr, "PacketAliasRedirectPort(): "
2385                         "call to AddLink() failed\n");
2386     }
2387 #endif
2388 
2389     return link;
2390 }
2391 
2392 /* Add server to the pool of servers */
2393 int
2394 PacketAliasAddServer(struct alias_link *link, struct in_addr addr, u_short port)
2395 {
2396     struct server *server;
2397 
2398     server = malloc(sizeof(struct server));
2399 
2400     if (server != NULL) {
2401 	struct server *head;
2402 
2403 	server->addr = addr;
2404 	server->port = port;
2405 
2406 	head = link->server;
2407 	if (head == NULL)
2408 	    server->next = server;
2409 	else {
2410 	    struct server *s;
2411 
2412 	    for (s = head; s->next != head; s = s->next);
2413 	    s->next = server;
2414 	    server->next = head;
2415 	}
2416 	link->server = server;
2417 	return (0);
2418     } else
2419 	return (-1);
2420 }
2421 
2422 /* Redirect packets of a given IP protocol from a specific
2423    public address to a private address */
2424 struct alias_link *
2425 PacketAliasRedirectProto(struct in_addr src_addr,
2426                          struct in_addr dst_addr,
2427                          struct in_addr alias_addr,
2428                          u_char proto)
2429 {
2430     struct alias_link *link;
2431 
2432     link = AddLink(src_addr, dst_addr, alias_addr,
2433                    NO_SRC_PORT, NO_DEST_PORT, 0,
2434                    proto);
2435 
2436     if (link != NULL)
2437     {
2438         link->flags |= LINK_PERMANENT;
2439     }
2440 #ifdef DEBUG
2441     else
2442     {
2443         fprintf(stderr, "PacketAliasRedirectProto(): "
2444                         "call to AddLink() failed\n");
2445     }
2446 #endif
2447 
2448     return link;
2449 }
2450 
2451 /* Static address translation */
2452 struct alias_link *
2453 PacketAliasRedirectAddr(struct in_addr src_addr,
2454                         struct in_addr alias_addr)
2455 {
2456     struct alias_link *link;
2457 
2458     link = AddLink(src_addr, nullAddress, alias_addr,
2459                    0, 0, 0,
2460                    LINK_ADDR);
2461 
2462     if (link != NULL)
2463     {
2464         link->flags |= LINK_PERMANENT;
2465     }
2466 #ifdef DEBUG
2467     else
2468     {
2469         fprintf(stderr, "PacketAliasRedirectAddr(): "
2470                         "call to AddLink() failed\n");
2471     }
2472 #endif
2473 
2474     return link;
2475 }
2476 
2477 
2478 void
2479 PacketAliasRedirectDelete(struct alias_link *link)
2480 {
2481 /* This is a dangerous function to put in the API,
2482    because an invalid pointer can crash the program. */
2483 
2484     deleteAllLinks = 1;
2485     DeleteLink(link);
2486     deleteAllLinks = 0;
2487 }
2488 
2489 
2490 void
2491 PacketAliasSetAddress(struct in_addr addr)
2492 {
2493     if (packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE
2494      && aliasAddress.s_addr != addr.s_addr)
2495         CleanupAliasData();
2496 
2497     aliasAddress = addr;
2498 }
2499 
2500 
2501 void
2502 PacketAliasSetTarget(struct in_addr target_addr)
2503 {
2504     targetAddress = target_addr;
2505 }
2506 
2507 
2508 void
2509 PacketAliasInit(void)
2510 {
2511     int i;
2512     struct timeval tv;
2513     struct timezone tz;
2514     static int firstCall = 1;
2515 
2516     if (firstCall == 1)
2517     {
2518         gettimeofday(&tv, &tz);
2519         timeStamp = tv.tv_sec;
2520         lastCleanupTime = tv.tv_sec;
2521         houseKeepingResidual = 0;
2522 
2523         for (i=0; i<LINK_TABLE_OUT_SIZE; i++)
2524             LIST_INIT(&linkTableOut[i]);
2525         for (i=0; i<LINK_TABLE_IN_SIZE; i++)
2526             LIST_INIT(&linkTableIn[i]);
2527 
2528         atexit(PacketAliasUninit);
2529         firstCall = 0;
2530     }
2531     else
2532     {
2533         deleteAllLinks = 1;
2534         CleanupAliasData();
2535         deleteAllLinks = 0;
2536     }
2537 
2538     aliasAddress.s_addr = INADDR_ANY;
2539     targetAddress.s_addr = INADDR_ANY;
2540 
2541     icmpLinkCount = 0;
2542     udpLinkCount = 0;
2543     tcpLinkCount = 0;
2544     pptpLinkCount = 0;
2545     protoLinkCount = 0;
2546     fragmentIdLinkCount = 0;
2547     fragmentPtrLinkCount = 0;
2548     sockCount = 0;
2549 
2550     cleanupIndex =0;
2551 
2552     packetAliasMode = PKT_ALIAS_SAME_PORTS
2553                     | PKT_ALIAS_USE_SOCKETS
2554                     | PKT_ALIAS_RESET_ON_ADDR_CHANGE;
2555 }
2556 
2557 void
2558 PacketAliasUninit(void) {
2559     deleteAllLinks = 1;
2560     CleanupAliasData();
2561     deleteAllLinks = 0;
2562     UninitPacketAliasLog();
2563 #ifndef NO_FW_PUNCH
2564     UninitPunchFW();
2565 #endif
2566 }
2567 
2568 
2569 /* Change mode for some operations */
2570 unsigned int
2571 PacketAliasSetMode(
2572     unsigned int flags, /* Which state to bring flags to */
2573     unsigned int mask   /* Mask of which flags to affect (use 0 to do a
2574                            probe for flag values) */
2575 )
2576 {
2577 /* Enable logging? */
2578     if (flags & mask & PKT_ALIAS_LOG)
2579     {
2580         InitPacketAliasLog();     /* Do the enable */
2581     } else
2582 /* _Disable_ logging? */
2583     if (~flags & mask & PKT_ALIAS_LOG) {
2584         UninitPacketAliasLog();
2585     }
2586 
2587 #ifndef NO_FW_PUNCH
2588 /* Start punching holes in the firewall? */
2589     if (flags & mask & PKT_ALIAS_PUNCH_FW) {
2590         InitPunchFW();
2591     } else
2592 /* Stop punching holes in the firewall? */
2593     if (~flags & mask & PKT_ALIAS_PUNCH_FW) {
2594         UninitPunchFW();
2595     }
2596 #endif
2597 
2598 /* Other flags can be set/cleared without special action */
2599     packetAliasMode = (flags & mask) | (packetAliasMode & ~mask);
2600     return packetAliasMode;
2601 }
2602 
2603 
2604 int
2605 PacketAliasCheckNewLink(void)
2606 {
2607     return newDefaultLink;
2608 }
2609 
2610 
2611 #ifndef NO_FW_PUNCH
2612 
2613 /*****************
2614   Code to support firewall punching.  This shouldn't really be in this
2615   file, but making variables global is evil too.
2616   ****************/
2617 
2618 /* Firewall include files */
2619 #include <net/if.h>
2620 #include <netinet/ip_fw.h>
2621 #include <string.h>
2622 #include <err.h>
2623 
2624 static void ClearAllFWHoles(void);
2625 
2626 static int fireWallBaseNum;     /* The first firewall entry free for our use */
2627 static int fireWallNumNums;     /* How many entries can we use? */
2628 static int fireWallActiveNum;   /* Which entry did we last use? */
2629 static char *fireWallField;     /* bool array for entries */
2630 
2631 #define fw_setfield(field, num)                         \
2632 do {                                                    \
2633     (field)[(num) - fireWallBaseNum] = 1;               \
2634 } /*lint -save -e717 */ while(0) /*lint -restore */
2635 #define fw_clrfield(field, num)                         \
2636 do {                                                    \
2637     (field)[(num) - fireWallBaseNum] = 0;               \
2638 } /*lint -save -e717 */ while(0) /*lint -restore */
2639 #define fw_tstfield(field, num) ((field)[(num) - fireWallBaseNum])
2640 
2641 void
2642 PacketAliasSetFWBase(unsigned int base, unsigned int num) {
2643     fireWallBaseNum = base;
2644     fireWallNumNums = num;
2645 }
2646 
2647 static void
2648 InitPunchFW(void) {
2649     fireWallField = malloc(fireWallNumNums);
2650     if (fireWallField) {
2651         memset(fireWallField, 0, fireWallNumNums);
2652         if (fireWallFD < 0) {
2653             fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
2654         }
2655         ClearAllFWHoles();
2656         fireWallActiveNum = fireWallBaseNum;
2657     }
2658 }
2659 
2660 static void
2661 UninitPunchFW(void) {
2662     ClearAllFWHoles();
2663     if (fireWallFD >= 0)
2664         close(fireWallFD);
2665     fireWallFD = -1;
2666     if (fireWallField)
2667         free(fireWallField);
2668     fireWallField = NULL;
2669     packetAliasMode &= ~PKT_ALIAS_PUNCH_FW;
2670 }
2671 
2672 /* Make a certain link go through the firewall */
2673 void
2674 PunchFWHole(struct alias_link *link) {
2675     int r;                      /* Result code */
2676     struct ip_fw rule;          /* On-the-fly built rule */
2677     int fwhole;                 /* Where to punch hole */
2678 
2679 /* Don't do anything unless we are asked to */
2680     if ( !(packetAliasMode & PKT_ALIAS_PUNCH_FW) ||
2681          fireWallFD < 0 ||
2682          link->link_type != LINK_TCP)
2683         return;
2684 
2685     memset(&rule, 0, sizeof rule);
2686 
2687 /** Build rule **/
2688 
2689     /* Find empty slot */
2690     for (fwhole = fireWallActiveNum;
2691          fwhole < fireWallBaseNum + fireWallNumNums &&
2692              fw_tstfield(fireWallField, fwhole);
2693          fwhole++)
2694         ;
2695     if (fwhole == fireWallBaseNum + fireWallNumNums) {
2696         for (fwhole = fireWallBaseNum;
2697              fwhole < fireWallActiveNum &&
2698                  fw_tstfield(fireWallField, fwhole);
2699              fwhole++)
2700             ;
2701         if (fwhole == fireWallActiveNum) {
2702             /* No rule point empty - we can't punch more holes. */
2703             fireWallActiveNum = fireWallBaseNum;
2704 #ifdef DEBUG
2705             fprintf(stderr, "libalias: Unable to create firewall hole!\n");
2706 #endif
2707             return;
2708         }
2709     }
2710     /* Start next search at next position */
2711     fireWallActiveNum = fwhole+1;
2712 
2713     /* Build generic part of the two rules */
2714     rule.fw_number = fwhole;
2715     IP_FW_SETNSRCP(&rule, 1);	/* Number of source ports. */
2716     IP_FW_SETNDSTP(&rule, 1);	/* Number of destination ports. */
2717     rule.fw_flg = IP_FW_F_ACCEPT | IP_FW_F_IN | IP_FW_F_OUT;
2718     rule.fw_prot = IPPROTO_TCP;
2719     rule.fw_smsk.s_addr = INADDR_BROADCAST;
2720     rule.fw_dmsk.s_addr = INADDR_BROADCAST;
2721 
2722     /* Build and apply specific part of the rules */
2723     rule.fw_src = GetOriginalAddress(link);
2724     rule.fw_dst = GetDestAddress(link);
2725     rule.fw_uar.fw_pts[0] = ntohs(GetOriginalPort(link));
2726     rule.fw_uar.fw_pts[1] = ntohs(GetDestPort(link));
2727 
2728     /* Skip non-bound links - XXX should not be strictly necessary,
2729        but seems to leave hole if not done.  Leak of non-bound links?
2730        (Code should be left even if the problem is fixed - it is a
2731        clear optimization) */
2732     if (rule.fw_uar.fw_pts[0] != 0 && rule.fw_uar.fw_pts[1] != 0) {
2733         r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
2734 #ifdef DEBUG
2735         if (r)
2736             err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)");
2737 #endif
2738         rule.fw_src = GetDestAddress(link);
2739         rule.fw_dst = GetOriginalAddress(link);
2740         rule.fw_uar.fw_pts[0] = ntohs(GetDestPort(link));
2741         rule.fw_uar.fw_pts[1] = ntohs(GetOriginalPort(link));
2742         r = setsockopt(fireWallFD, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
2743 #ifdef DEBUG
2744         if (r)
2745             err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
2746 #endif
2747     }
2748 /* Indicate hole applied */
2749     link->data.tcp->fwhole = fwhole;
2750     fw_setfield(fireWallField, fwhole);
2751 }
2752 
2753 /* Remove a hole in a firewall associated with a particular alias
2754    link.  Calling this too often is harmless. */
2755 static void
2756 ClearFWHole(struct alias_link *link) {
2757     if (link->link_type == LINK_TCP) {
2758         int fwhole =  link->data.tcp->fwhole; /* Where is the firewall hole? */
2759         struct ip_fw rule;
2760 
2761         if (fwhole < 0)
2762             return;
2763 
2764         memset(&rule, 0, sizeof rule);
2765         rule.fw_number = fwhole;
2766         while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
2767             ;
2768         fw_clrfield(fireWallField, fwhole);
2769         link->data.tcp->fwhole = -1;
2770     }
2771 }
2772 
2773 /* Clear out the entire range dedicated to firewall holes. */
2774 static void
2775 ClearAllFWHoles(void) {
2776     struct ip_fw rule;          /* On-the-fly built rule */
2777     int i;
2778 
2779     if (fireWallFD < 0)
2780         return;
2781 
2782     memset(&rule, 0, sizeof rule);
2783     for (i = fireWallBaseNum; i < fireWallBaseNum + fireWallNumNums; i++) {
2784         rule.fw_number = i;
2785         while (!setsockopt(fireWallFD, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule))
2786             ;
2787     }
2788     memset(fireWallField, 0, fireWallNumNums);
2789 }
2790 #endif
2791