aboutsummaryrefslogtreecommitdiffstats
path: root/net/dante/files/patch-sockd-getifa.c
blob: d6c6eecf51a3a2b32292f86c1632d4021d488724 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
--- sockd/getifa.c.orig 2003-07-01 13:21:40.000000000 +0000
+++ sockd/getifa.c  2009-01-18 23:43:40.000000000 +0000
@@ -58,6 +58,9 @@
  * that will be selected by the OS to connect to that destination address.
  *==========================================================================*/
 #include   <net/route.h>           /* RTA_xxx constants */
+#if HAVE_ROUTEINFO_BSD
+#include   <net/if_dl.h>
+#endif /* HAVE_ROUTEINFO_BSD */
 #if HAVE_ROUTEINFO_LINUX
 #include   <asm/types.h>
 #include   <linux/netlink.h>
@@ -233,9 +236,11 @@
    pid_t               pid;
    struct rt_msghdr *rtm;
    struct sockaddr *sa;
+   char *cp;
+   int i;
    uid_t euid;
    struct in_addr inaddr_none;
-   struct in_addr gwaddr;
+   struct sockaddr ifa;
 
    inaddr_none.s_addr = htonl(INADDR_NONE);
 
@@ -256,85 +261,123 @@
    }
 
    /*===================================================================
-    * Do until 2 consecutive RTM_GET return the same gateway address.
-    * The first calls may return router addresses in RTA_GATEWAY and
-    * those will not match any local external address.
+    * Do a RTM_GET to look up the gateway for the address.
+    * Extract the RTM_IFA from it, which is the source address used
+    * on that interface.
+    * The code is modeled after the code for route(8).
     *==================================================================*/
+   /*===========================================================
+    * Build the necessary data structures to get routing info.
+    * The structures are:
+    *   rt_msghdr - Specifies RTM_GET for getting routing table
+    *      info
+    *   sockaddr - contains the destination address
+    *   sockaddr_dl - necessary for unkown reasons.
+    *==========================================================*/
+#define ROUNDUP(a) \
+   ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+
+   bzero(buf, sizeof(buf));
+   rtm                 = (struct rt_msghdr *) buf;
+   rtm->rtm_version    = RTM_VERSION;
+   rtm->rtm_type       = RTM_GET;
+   rtm->rtm_flags      = RTF_UP | RTF_HOST | RTF_GATEWAY | RTF_STATIC;
+   rtm->rtm_addrs      = RTA_DST | RTA_IFA;
+   rtm->rtm_pid        = pid = getpid();
+   rtm->rtm_seq        = SEQ;
+
+   cp  = (char *) (rtm + 1);
+   sa = (struct sockaddr *) cp;
+   /* LINTED pointer casts may be troublesome */
+   TOIN(sa)->sin_family    = AF_INET;
+   /* LINTED pointer casts may be troublesome */
+   TOIN(sa)->sin_addr  = destaddr;
+   /* LINTED pointer casts may be troublesome */
+   TOIN(sa)->sin_port  = htons(0);
+   sa->sa_len = sizeof(struct sockaddr_in);
+   ADVANCE(cp, sa);
+   sa = (struct sockaddr *) cp;
+   /* add dummy link layer address */
+   sa->sa_family = AF_LINK;
+   sa->sa_len = sizeof(struct sockaddr_dl);
+   ADVANCE(cp, sa);
+   sa = (struct sockaddr *) cp;
+   rtm->rtm_addrs |= RTA_IFP;
+
+   rtm->rtm_msglen = cp - buf;
+
+   /*===========================================================
+    * Send the request and get the response.
+    *==========================================================*/
+   if (write(sockfd, rtm, (size_t)rtm->rtm_msglen) != rtm->rtm_msglen) {
+       swarn("%s: write() to AF_ROUTE failed", function);
+       close(sockfd);
+       return inaddr_none;
+   }
+
    do {
-       /*===========================================================
-        * Build the necessary data structures to get routing info.
-        * The structures are:
-        *   rt_msghdr - Specifies RTM_GET for getting routing table
-        *      info
-        *   sockaddr - contains the destination address
-        *==========================================================*/
-
-       bzero(buf, sizeof(buf));
-       rtm                 = (struct rt_msghdr *) buf;
-       rtm->rtm_msglen = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in);
-       rtm->rtm_version    = RTM_VERSION;
-       rtm->rtm_type       = RTM_GET;
-       rtm->rtm_addrs      = RTA_DST;
-       rtm->rtm_pid        = pid = getpid();
-       rtm->rtm_seq        = SEQ;
-
-       sa                          = (struct sockaddr *) (rtm + 1);
-       /* LINTED pointer casts may be troublesome */
-       TOIN(sa)->sin_family    = AF_INET;
-       /* LINTED pointer casts may be troublesome */
-       TOIN(sa)->sin_addr  = destaddr;
-       /* LINTED pointer casts may be troublesome */
-       TOIN(sa)->sin_port  = htons(0);
-
-       /*===========================================================
-        * Send the request and get the response.
-        *==========================================================*/
-       if (write(sockfd, rtm, (size_t)rtm->rtm_msglen) != rtm->rtm_msglen) {
-           swarn("%s: write() to AF_ROUTE failed", function);
+       if (read(sockfd, rtm, sizeof(buf)) == -1) {
+           swarn("%s: read from AF_ROUTE failed", function);
            close(sockfd);
            return inaddr_none;
        }
+   } while (rtm->rtm_type != RTM_GET || rtm->rtm_seq != SEQ
+   || rtm->rtm_pid != pid);
 
-       do {
-           if (read(sockfd, rtm, sizeof(buf)) == -1) {
-               swarn("%s: read from AF_ROUTE failed", function);
+   /*================================================================
+    * Iterate over the address structure extracting only the relevant
+    * addresses.
+    *===============================================================*/
+   cp  = (char *) (rtm + 1);
+   sa = (struct sockaddr *) cp;
+
+   for (i=0; (i < RTAX_MAX) && (cp < buf + sizeof(buf)); i++) {
+       switch (i) {
+       case RTAX_GATEWAY:
+           if (!(rtm->rtm_addrs & RTA_GATEWAY)) {
+               slog(LOG_DEBUG, "%s: can't find gateway for %s, using defaultexternal",
+               function, inet_ntoa(destaddr));
                close(sockfd);
-               return inaddr_none;
+               return getdefaultexternal();
            }
-       } while (rtm->rtm_type != RTM_GET || rtm->rtm_seq != SEQ
-       || rtm->rtm_pid != pid);
-
-       /*============================================================
-        * Go straight to the RTA_GATEWAY entry for our info.
-        *===========================================================*/
-       sa = (struct sockaddr *) (rtm + 1);
-
-       if (!(rtm->rtm_addrs & RTA_GATEWAY)
-       ||  (sa += RTAX_GATEWAY) == NULL
-       ||  TOIN(sa)->sin_family != AF_INET) {
-           slog(LOG_DEBUG, "%s: can't find gateway for %s, using defaultexternal",
-           function, inet_ntoa(destaddr));
-           close(sockfd);
-           return getdefaultexternal();
+           break;
+       case RTAX_IFA:
+           if (!(rtm->rtm_addrs & RTA_IFA)
+               ||  TOIN(sa)->sin_family != AF_INET) {
+               slog(LOG_DEBUG, "%s: can't find ifa for %s, using defaultexternal",
+               function, inet_ntoa(destaddr));
+               close(sockfd);
+               return getdefaultexternal();
+           }
+           else {
+               memcpy((void*)&ifa, (void*)sa, sa->sa_len);
+           }
+           break;
+       }
+       if (rtm->rtm_addrs & (1<<i)) {
+           ADVANCE(cp, sa);
+           sa = (struct sockaddr *)cp;
        }
+#undef ROUNDUP
+#undef ADVANCE
 
-       gwaddr.s_addr = destaddr.s_addr;
-       destaddr.s_addr = TOIN(sa)->sin_addr.s_addr;
-   } while (destaddr.s_addr != gwaddr.s_addr);
+   }
 
    close(sockfd);
 
-   if (!isonexternal(sa)) {
+   if (!isonexternal(&ifa)) {
        char a[MAXSOCKADDRSTRING];
 
        swarnx("%s: address %s selected, but not set for external interface",
-       function, sockaddr2string(sa, a, sizeof(a)));
+       function, sockaddr2string(&ifa, a, sizeof(a)));
 
        return getdefaultexternal();
    }
 
    /* LINTED pointer casts may be troublesome */
-   return TOIN(sa)->sin_addr;
+   return TOIN(&ifa)->sin_addr;
 }
 
 #else /* HAVE_ROUTEINFO_BSD */
@@ -393,7 +436,7 @@
        struct sockaddr check;
        int match = 0;
 
-       switch ((*sockscf.external.addrv).atype) {
+       switch (sockscf.external.addrv[i].atype) {
            case SOCKS_ADDR_IFNAME: {
                int ifi;