1/* 2 * Copyright (c) 1988, 1990, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include <sys/types.h> 35#include <sys/socket.h> 36 37#include <netdb.h> 38#include <netinet/in.h> 39#include <netinet/ip.h> 40 41#include <stdio.h> 42#include <stdlib.h> 43#include <string.h> 44#include <errno.h> 45 46/* 47 * 5/10/12: Imported from telnet project 48 * 49 * Source route is handed in as 50 * [!]@hop1@hop2...[@|:]dst 51 * If the leading ! is present, it is a 52 * strict source route, otherwise it is 53 * assmed to be a loose source route. 54 * 55 * We fill in the source route option as 56 * hop1,hop2,hop3...dest 57 * and return a pointer to hop1, which will 58 * be the address to connect() to. 59 * 60 * Arguments: 61 * 62 * res: ponter to addrinfo structure which contains sockaddr to 63 * the host to connect to. 64 * 65 * arg: pointer to route list to decipher 66 * 67 * cpp: If *cpp is not equal to NULL, this is a 68 * pointer to a pointer to a character array 69 * that should be filled in with the option. 70 * 71 * lenp: pointer to an integer that contains the 72 * length of *cpp if *cpp != NULL. 73 * 74 * protop: pointer to an integer that should be filled in with 75 * appropriate protocol for setsockopt, as socket 76 * protocol family. 77 * 78 * optp: pointer to an integer that should be filled in with 79 * appropriate option for setsockopt, as socket protocol 80 * family. 81 * 82 * Return values: 83 * 84 * If the return value is 1, then all operations are 85 * successful. If the 86 * return value is -1, there was a syntax error in the 87 * option, either unknown characters, or too many hosts. 88 * If the return value is 0, one of the hostnames in the 89 * path is unknown, and *cpp is set to point to the bad 90 * hostname. 91 * 92 * *cpp: If *cpp was equal to NULL, it will be filled 93 * in with a pointer to our static area that has 94 * the option filled in. This will be 32bit aligned. 95 * 96 * *lenp: This will be filled in with how long the option 97 * pointed to by *cpp is. 98 * 99 * *protop: This will be filled in with appropriate protocol for 100 * setsockopt, as socket protocol family. 101 * 102 * *optp: This will be filled in with appropriate option for 103 * setsockopt, as socket protocol family. 104 */ 105int 106sourceroute(struct addrinfo *ai, char *arg, char **cpp, int *lenp, int *protop, int *optp) 107{ 108 static char buf[1024 + ALIGNBYTES]; /*XXX*/ 109 char *cp, *cp2, *lsrp, *ep; 110 struct sockaddr_in *_sin; 111#ifdef INET6 112 struct sockaddr_in6 *sin6; 113 struct cmsghdr *cmsg = NULL; 114#endif 115 struct addrinfo hints, *res; 116 int error; 117 char c; 118 119 /* 120 * Verify the arguments, and make sure we have 121 * at least 7 bytes for the option. 122 */ 123 if (cpp == NULL || lenp == NULL) 124 return -1; 125 if (*cpp != NULL) { 126 switch (ai->ai_family) { 127 case AF_INET: 128 if (*lenp < 7) 129 return -1; 130 break; 131#ifdef INET6 132 case AF_INET6: 133 if (*lenp < (int)CMSG_SPACE(sizeof(struct ip6_rthdr) + 134 sizeof(struct in6_addr))) 135 return -1; 136 break; 137#endif 138 } 139 } 140 /* 141 * Decide whether we have a buffer passed to us, 142 * or if we need to use our own static buffer. 143 */ 144 if (*cpp) { 145 lsrp = *cpp; 146 ep = lsrp + *lenp; 147 } else { 148 *cpp = lsrp = (char *)ALIGN(buf); 149 ep = lsrp + 1024; 150 } 151 152 cp = arg; 153 154#ifdef INET6 155 if (ai->ai_family == AF_INET6) { 156 cmsg = inet6_rthdr_init(*cpp, IPV6_RTHDR_TYPE_0); 157 if (*cp != '@') 158 return -1; 159 *protop = IPPROTO_IPV6; 160 *optp = IPV6_PKTOPTIONS; 161 } else 162#endif 163 { 164 /* 165 * Next, decide whether we have a loose source 166 * route or a strict source route, and fill in 167 * the begining of the option. 168 */ 169 if (*cp == '!') { 170 cp++; 171 *lsrp++ = IPOPT_SSRR; 172 } else 173 *lsrp++ = IPOPT_LSRR; 174 175 if (*cp != '@') 176 return -1; 177 178 lsrp++; /* skip over length, we'll fill it in later */ 179 *lsrp++ = 4; 180 *protop = IPPROTO_IP; 181 *optp = IP_OPTIONS; 182 } 183 184 cp++; 185 memset(&hints, 0, sizeof(hints)); 186 hints.ai_family = ai->ai_family; 187 hints.ai_socktype = SOCK_STREAM; 188 for (c = 0;;) { 189 if ( 190#ifdef INET6 191 ai->ai_family != AF_INET6 && 192#endif 193 c == ':') 194 cp2 = 0; 195 else for (cp2 = cp; (c = *cp2); cp2++) { 196 if (c == ',') { 197 *cp2++ = '\0'; 198 if (*cp2 == '@') 199 cp2++; 200 } else if (c == '@') { 201 *cp2++ = '\0'; 202 } else if ( 203#ifdef INET6 204 ai->ai_family != AF_INET6 && 205#endif 206 c == ':') { 207 *cp2++ = '\0'; 208 } else 209 continue; 210 break; 211 } 212 if (!c) 213 cp2 = 0; 214 215 hints.ai_flags = AI_NUMERICHOST; 216 error = getaddrinfo(cp, NULL, &hints, &res); 217#ifdef EAI_NODATA 218 if ((error == EAI_NODATA) || (error == EAI_NONAME)) 219#else 220 if (error == EAI_NONAME) 221#endif 222 { 223 hints.ai_flags = 0; 224 error = getaddrinfo(cp, NULL, &hints, &res); 225 } 226 if (error != 0) { 227 fprintf(stderr, "%s: %s\n", cp, gai_strerror(error)); 228 if (error == EAI_SYSTEM) 229 fprintf(stderr, "%s: %s\n", cp, 230 strerror(errno)); 231 *cpp = cp; 232 return(0); 233 } 234#ifdef INET6 235 if (res->ai_family == AF_INET6) { 236 sin6 = (struct sockaddr_in6 *)res->ai_addr; 237 inet6_rthdr_add(cmsg, &sin6->sin6_addr, IPV6_RTHDR_LOOSE); 238 } else 239#endif 240 { 241 _sin = (struct sockaddr_in *)res->ai_addr; 242 memcpy(lsrp, (char *)&_sin->sin_addr, 4); 243 lsrp += 4; 244 } 245 if (cp2) 246 cp = cp2; 247 else 248 break; 249 /* 250 * Check to make sure there is space for next address 251 */ 252#ifdef INET6 253 if (res->ai_family == AF_INET6) { 254 if (((char *)CMSG_DATA(cmsg) + 255 sizeof(struct ip6_rthdr) + 256 ((inet6_rthdr_segments(cmsg) + 1) * 257 sizeof(struct in6_addr))) > ep) 258 return -1; 259 } else 260#endif 261 if (lsrp + 4 > ep) 262 return -1; 263 freeaddrinfo(res); 264 } 265#ifdef INET6 266 if (res->ai_family == AF_INET6) { 267 inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE); 268 *lenp = cmsg->cmsg_len; 269 } else 270#endif 271 { 272 if ((*(*cpp+IPOPT_OLEN) = lsrp - *cpp) <= 7) { 273 *cpp = 0; 274 *lenp = 0; 275 return -1; 276 } 277 *lsrp++ = IPOPT_NOP; /* 32 bit word align it */ 278 *lenp = lsrp - *cpp; 279 } 280 freeaddrinfo(res); 281 return 1; 282}