In message <cac38b59-1f54-4788-87a2-a1a8be453...@delong.com>, Owen DeLong write s: > >=20 > > It's deeper than just that, though. The whole paradigm is messy, from > > the point of view of someone who just wants to get stuff done. The > > examples are (almost?) all fatally flawed. The code that actually = > gets > > at least some of it right ends up being too complex and too hard for > > people to understand why things are done the way they are. > >=20 > > Even in the "old days", before IPv6, geez, look at this: > >=20 > > bcopy(host->h_addr_list[n], (char *)&addr->sin_addr.s_addr, = > sizeof(addr->sin_addr.s_addr)); > >=20 > > That's real comprehensible - and it's essentially the data interface=20= > > > between the resolver library and the system's addressing structures > > for syscalls. > >=20 > > On one hand, it's "great" that they wanted to abstract the dirty = > details > > of DNS away from users, but I'd say they failed pretty much even at = > that. > >=20 > > ... JG > > --=20 > > Joe Greco - sol.net Network Services - Milwaukee, WI - = > http://www.sol.net > > "We call it the 'one bite at the apple' rule. Give me one chance [and] = > then I > > won't contact you again." - Direct Marketing Ass'n position on e-mail = > spam(CNN) > > With 24 million small businesses in the US alone, that's way too many = > apples. > > I think that the modern set of getaddrinfo and connect is actually not = > that complicated: > > /* Hints for getaddrinfo() (tell it what we want) */ > memset(&addrinfo, 0, sizeof(addrinfo)); /* Zero out the buffer = > */ > addrinfo.ai_family=3DPF_UNSPEC; /* Any and all = > address families */ > addrinfo.ai_socktype=3DSOCK_STREAM; /* Stream Socket */ > addrinfo.ai_protocol=3DIPPROTO_TCP; /* TCP */ > /* Ask the resolver library for the information. Exit on failure. */ > /* argv[1] is the hostname passed in by the user. "demo" is the = > service name */ > if (rval =3D getaddrinfo(argv[1], "demo", &addrinfo, &res) !=3D 0) { > fprintf(stderr, "%s: Failed to resolve address information.\n", = > argv[0]); > exit(2); > } > > /* Iterate through the results */ > for (r=3Dres; r; r =3D r->ai_next) { > /* Create a socket configured for the next candidate */ > sockfd6 =3D socket(r->ai_family, r->ai_socktype, r->ai_protocol); > /* Try to connect */ > if (connect(sockfd6, r->ai_addr, r->ai_addrlen) < 0) > { > /* Failed to connect */ > e_save =3D errno; > /* Destroy socket */ > (void) close(sockfd6); > /* Recover the error information */ > errno =3D e_save; > /* Tell the user that this attempt failed */ > fprintf(stderr, "%s: Failed attempt to %s.\n", argv[0],=20 > get_ip_str((struct sockaddr *)r->ai_addr, buf, BUFLEN)); > /* Give error details */ > perror("Socket error"); > } else { /* Success! */ > /* Inform the user */ > snprintf(s, BUFLEN, "%s: Succeeded to %s.", argv[0], > get_ip_str((struct sockaddr *)r->ai_addr, buf, BUFLEN)); > debug(5, argv[0], s); > /* Flag our success */ > success++; > /* Stop iterating */ > break; > } > } > /* Out of the loop. Either we succeeded or ran out of possibilities */ > if (success =3D=3D 0) /* If we ran out of possibilities... */ > { > /* Inform the user, free up the resources, and exit */ > fprintf(stderr, "%s: Failed to connect to %s.\n", argv[0], argv[1]); > freeaddrinfo(res); > exit(5); > } > /* Succeeded. Inform the user and continue with the application */ > printf("%s: Successfully connected to %s at %s on FD %d.\n", argv[0], = > argv[1], > get_ip_str((struct sockaddr *)r->ai_addr, buf, BUFLEN), > sockfd6); > /* Free up the memory held by the resolver results */ > freeaddrinfo(res); > > It's really hard to make a case that this is all that complex. > > I put a lot of extra comments in there to make it clear what's happening = > for people who may not be used to coding in C. It also contains a whole = > lot of extra user notification and debugging instrumentation because it = > is designed as an example people can use to learn with.=20 > > Yes, this was a lot messier and a lot stranger and harder to get right = > with get*by{name,addr}, but, those days are long gone and anyone still = > coding with those needs to move forward. > > Owen >
These days you want something more complicated as everyone is or will be soon multi-homed. The basic loop above has very bad error characteristics if the first machines are not reachable. I've got working select, poll and thread based examples here: http://www.isc.org/community/blog/201101/how-to-connect-to-a-multi-homed-server-over-tcp. >From http://www.isc.org/files/imce/select-connect_0.c: /* * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #define TIMEOUT 500 /* ms */ int connect_to_host(struct addrinfo *res0) { struct addrinfo *res; int fd = -1, n, i, j, flags, count, max = -1, *fds; struct timeval *timeout, timeout0 = { 0, TIMEOUT * 1000}; fd_set fdset, wrset; /* * Work out how many possible descriptors we could use. */ for (res = res0, count = 0; res; res = res->ai_next) count++; fds = calloc(count, sizeof(*fds)); if (fds == NULL) { perror("calloc"); goto cleanup; } FD_ZERO(&fdset); for (res = res0, i = 0, count = 0; res; res = res->ai_next) { fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (fd == -1) { /* * If AI_ADDRCONFIG is not supported we will get * EAFNOSUPPORT returned. Behave as if the address * was not there. */ if (errno != EAFNOSUPPORT) perror("socket"); else if (res->ai_next != NULL) continue; } else if (fd >= FD_SETSIZE) { close(fd); } else if ((flags = fcntl(fd, F_GETFL)) == -1) { perror("fcntl"); close(fd); } else if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { perror("fcntl"); close(fd); } else if (connect(fd, res->ai_addr, res->ai_addrlen) == -1) { if (errno != EINPROGRESS) { perror("connect"); close(fd); } else { /* * Record the information for this descriptor. */ fds[i] = fd; FD_SET(fd, &fdset); if (max == -1 || fd > max) max = fd; count++; i++; } } else { /* * We connected without blocking. */ goto done; } if (count == 0) continue; assert(max != -1); do { if (res->ai_next != NULL) timeout = &timeout0; else timeout = NULL; /* The write bit is set on both success and failure. */ wrset = fdset; n = select(max + 1, NULL, &wrset, NULL, timeout); if (n == 0) { timeout0.tv_usec >>= 1; break; } if (n < 0) { if (errno == EAGAIN || errno == EINTR) continue; perror("select"); fd = -1; goto done; } for (fd = 0; fd <= max; fd++) { if (FD_ISSET(fd, &wrset)) { socklen_t len; int err; for (j = 0; j < i; j++) if (fds[j] == fd) break; assert(j < i); /* * Test to see if the connect * succeeded. */ len = sizeof(err); n = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len); if (n != 0 || err != 0) { close(fd); FD_CLR(fd, &fdset); fds[j] = -1; count--; continue; } /* Connect succeeded. */ goto done; } } } while (timeout == NULL && count != 0); } /* We failed to connect. */ fd = -1; done: /* Close all other descriptors we have created. */ for (j = 0; j < i; j++) if (fds[j] != fd && fds[j] != -1) { close(fds[j]); } if (fd != -1) { /* Restore default blocking behaviour. */ if ((flags = fcntl(fd, F_GETFL)) != -1) { flags &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) == -1) perror("fcntl"); } else perror("fcntl"); } cleanup: /* Free everything. */ if (fds) free(fds); return (fd); } -- Mark Andrews, ISC 1 Seymour St., Dundas Valley, NSW 2117, Australia PHONE: +61 2 9871 4742 INTERNET: ma...@isc.org