52 #if HAVE_KRB5_MEMORY_KEYTAB
53 typedef struct _krb5_kt_list {
54 struct _krb5_kt_list *next;
55 krb5_keytab_entry *entry;
57 krb5_kt_list ktlist =
nullptr;
59 krb5_keytab memory_keytab;
61 krb5_error_code krb5_free_kt_list(krb5_context context, krb5_kt_list kt_list);
62 krb5_error_code krb5_write_keytab(krb5_context context,
65 krb5_error_code krb5_read_keytab(krb5_context context,
67 krb5_kt_list *kt_list);
71 check_k5_err(krb5_context context,
const char *
function, krb5_error_code
code)
76 errmsg = krb5_get_error_message(context,
code);
78 fprintf(stderr,
"%s| %s: ERROR: %s: %s\n",
LogTime(),
PROGRAM,
function, errmsg);
79 #if HAVE_KRB5_FREE_ERROR_MESSAGE
80 krb5_free_error_message(context, errmsg);
81 #elif HAVE_KRB5_FREE_ERROR_STRING
82 krb5_free_error_string(context, (
char *)errmsg);
97 struct addrinfo *hres =
nullptr, *hres_list;
100 rc = gethostname(hostname,
sizeof(hostname)-1);
102 debug((
char *)
"%s| %s: ERROR: resolving hostname '%s' failed\n",
LogTime(),
PROGRAM, hostname);
103 fprintf(stderr,
"%s| %s: ERROR: resolving hostname '%s' failed\n",
107 rc =
getaddrinfo(hostname,
nullptr,
nullptr, &hres);
108 if (rc != 0 || hres ==
nullptr ) {
109 debug((
char *)
"%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
112 "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
120 hres_list = hres_list->
ai_next;
123 sizeof(hostname),
nullptr, 0, 0);
125 debug((
char *)
"%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
128 "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
134 hostname[
sizeof(hostname)-1] =
'\0';
139 check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
140 const char *
function,
int log,
int sout)
142 if (GSS_ERROR(major_status)) {
143 OM_uint32 maj_stat, min_stat;
144 OM_uint32 msg_ctx = 0;
145 gss_buffer_desc status_string;
153 maj_stat = gss_display_status(&min_stat, major_status,
154 GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
155 if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
156 if (
sizeof(buf) > len + status_string.length + 1) {
157 snprintf(buf + len, (
sizeof(buf) - len),
"%s", (
char *) status_string.value);
158 len += status_string.length;
162 gss_release_buffer(&min_stat, &status_string);
164 if (
sizeof(buf) > len + 2) {
165 snprintf(buf + len, (
sizeof(buf) - len),
"%s",
". ");
171 maj_stat = gss_display_status(&min_stat, minor_status,
172 GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
173 if (maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
174 if (
sizeof(buf) > len + status_string.length) {
175 snprintf(buf + len, (
sizeof(buf) - len),
"%s", (
char *) status_string.value);
176 len += status_string.length;
180 gss_release_buffer(&min_stat, &status_string);
184 fprintf(stdout,
"BH %s failed: %s\n",
function, buf);
186 fprintf(stderr,
"%s| %s: INFO: User not authenticated\n",
LogTime(),
193 #if HAVE_KRB5_MEMORY_KEYTAB
197 krb5_error_code krb5_free_kt_list(krb5_context context, krb5_kt_list list)
199 krb5_kt_list lp = list;
202 #if USE_HEIMDAL_KRB5 || ( HAVE_KRB5_KT_FREE_ENTRY && HAVE_DECL_KRB5_KT_FREE_ENTRY )
203 krb5_error_code retval = krb5_kt_free_entry(context, lp->entry);
205 krb5_error_code retval = krb5_free_keytab_entry_contents(context, lp->entry);
208 if (
check_k5_err(context,
"krb5_kt_free_entry", retval))
210 krb5_kt_list prev = lp;
220 krb5_error_code krb5_read_keytab(krb5_context context,
char *name, krb5_kt_list *list)
222 krb5_kt_list lp =
nullptr, tail =
nullptr, back =
nullptr;
224 krb5_keytab_entry *entry;
225 krb5_kt_cursor cursor;
226 krb5_error_code retval = 0;
230 for (lp = *list; lp->next; lp = lp->next);
233 retval = krb5_kt_resolve(context, name, &kt);
236 retval = krb5_kt_start_seq_get(context, kt, &cursor);
237 if (
check_k5_err(context,
"krb5_kt_start_seq_get", retval))
240 entry = (krb5_keytab_entry *)
xcalloc(1,
sizeof (krb5_keytab_entry));
243 debug((
char *)
"%s| %s: ERROR: krb5_read_keytab failed: %s\n",
245 fprintf(stderr,
"%s| %s: ERROR: krb5_read_keytab: %s\n",
249 memset(entry, 0,
sizeof (*entry));
250 retval = krb5_kt_next_entry(context, kt, entry, &cursor);
251 if (
check_k5_err(context,
"krb5_kt_next_entry", retval))
255 lp = (krb5_kt_list)
xmalloc(
sizeof (*lp));
258 debug((
char *)
"%s| %s: ERROR: krb5_read_keytab failed: %s\n",
260 fprintf(stderr,
"%s| %s: ERROR: krb5_read_keytab: %s\n",
265 lp->next = (krb5_kt_list)
xmalloc(
sizeof (*lp));
268 debug((
char *)
"%s| %s: ERROR: krb5_read_keytab failed: %s\n",
270 fprintf(stderr,
"%s| %s: ERROR: krb5_read_keytab: %s\n",
283 if (retval == KRB5_KT_END)
286 krb5_free_kt_list(context, tail);
289 back->next =
nullptr;
294 krb5_kt_end_seq_get(context, kt, &cursor);
296 krb5_kt_close(context, kt);
303 krb5_error_code krb5_write_keytab(krb5_context context, krb5_kt_list list,
char *name)
306 krb5_error_code retval = 0;
308 snprintf(ktname,
sizeof(ktname),
"%s", name);
309 retval = krb5_kt_resolve(context, ktname, &memory_keytab);
312 for (krb5_kt_list lp = list; lp; lp = lp->next) {
313 retval = krb5_kt_add_entry(context, memory_keytab, lp->entry);
325 main(
int argc,
char *
const argv[])
329 char *user =
nullptr;
330 char *rfc_user =
nullptr;
332 char ad_groups[MAX_PAC_GROUP_SIZE];
336 gss_buffer_desc data_set = GSS_C_EMPTY_BUFFER;
338 gss_buffer_desc type_id = GSS_C_EMPTY_BUFFER;
341 krb5_context context =
nullptr;
345 int opt,
log = 0, norealm = 0;
346 OM_uint32 ret_flags = 0, spnego_flag = 0;
347 char *
service_name = (
char *)
"HTTP", *host_name =
nullptr;
348 char *token =
nullptr;
349 char *service_principal =
nullptr;
350 char *keytab_name =
nullptr;
351 char *keytab_name_env =
nullptr;
353 #if HAVE_KRB5_MEMORY_KEYTAB
354 char *memory_keytab_name =
nullptr;
355 char *memory_keytab_name_env =
nullptr;
357 char *rcache_type =
nullptr;
358 char *rcache_type_env =
nullptr;
359 char *rcache_dir =
nullptr;
360 char *rcache_dir_env =
nullptr;
361 OM_uint32 major_status, minor_status;
362 gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
363 gss_name_t client_name = GSS_C_NO_NAME;
364 gss_name_t server_name = GSS_C_NO_NAME;
365 gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL;
366 gss_buffer_desc service = GSS_C_EMPTY_BUFFER;
367 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
368 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
369 const unsigned char *kerberosToken =
nullptr;
370 const unsigned char *spnegoToken =
nullptr;
371 size_t spnegoTokenLength = 0;
373 setbuf(stdout,
nullptr);
374 setbuf(stdin,
nullptr);
376 while (-1 != (opt =
getopt(argc, argv,
"dirs:k:c:t:"))) {
395 fprintf(stderr,
"ERROR: keytab file not given\n");
402 if ((ktp=strchr(keytab_name,
':')))
406 if (stat((
const char*)ktp, &fstat)) {
408 fprintf(stderr,
"ERROR: keytab file %s does not exist\n",keytab_name);
410 fprintf(stderr,
"ERROR: Error %s during stat of keytab file %s\n",
strerror(errno),keytab_name);
412 }
else if (!S_ISREG(fstat.st_mode)) {
413 fprintf(stderr,
"ERROR: keytab file %s is not a file\n",keytab_name);
418 if (access(ktp, R_OK)) {
419 fprintf(stderr,
"ERROR: keytab file %s is not accessible\n",keytab_name);
431 fprintf(stderr,
"ERROR: replay cache directory not given\n");
438 if (stat((
const char*)rcache_dir, &dstat)) {
440 fprintf(stderr,
"ERROR: replay cache directory %s does not exist\n",rcache_dir);
442 fprintf(stderr,
"ERROR: Error %s during stat of replay cache directory %s\n",
strerror(errno),rcache_dir);
444 }
else if (!S_ISDIR(dstat.st_mode)) {
445 fprintf(stderr,
"ERROR: replay cache directory %s is not a directory\n",rcache_dir);
450 if (access(rcache_dir, W_OK)) {
451 fprintf(stderr,
"ERROR: replay cache directory %s is not accessible\n",rcache_dir);
460 fprintf(stderr,
"ERROR: replay cache type not given\n");
468 fprintf(stderr,
"ERROR: service principal not given\n");
473 fprintf(stderr,
"Usage: \n");
474 fprintf(stderr,
"squid_kerb_auth [-d] [-i] [-s SPN] [-k keytab] [-c rcdir] [-t rctype]\n");
475 fprintf(stderr,
"-d full debug\n");
476 fprintf(stderr,
"-i informational messages\n");
477 fprintf(stderr,
"-r remove realm from username\n");
478 fprintf(stderr,
"-s service principal name\n");
479 fprintf(stderr,
"-k keytab name\n");
480 fprintf(stderr,
"-c replay cache directory\n");
481 fprintf(stderr,
"-t replay cache type\n");
483 "The SPN can be set to GSS_C_NO_NAME to allow any entry from keytab\n");
484 fprintf(stderr,
"default SPN is HTTP/fqdn@DEFAULT_REALM\n");
490 if (service_principal && strcasecmp(service_principal,
"GSS_C_NO_NAME")) {
491 if (!strstr(service_principal,
"HTTP/")) {
492 debug((
char *)
"%s| %s: WARN: service_principal %s does not start with HTTP/\n",
495 service.value = service_principal;
496 service.length = strlen((
char *) service.value);
501 "%s| %s: FATAL: Local hostname could not be determined. Please specify the service principal\n",
503 fprintf(stdout,
"BH hostname error\n");
507 snprintf((
char *) service.value, strlen(
service_name) + strlen(host_name) + 2,
509 service.length = strlen((
char *) service.value);
514 rcache_type_env = (
char *)
xmalloc(strlen(
"KRB5RCACHETYPE=")+strlen(rcache_type)+1);
515 strcpy(rcache_type_env,
"KRB5RCACHETYPE=");
516 strcat(rcache_type_env, rcache_type);
517 putenv(rcache_type_env);
518 debug((
char *)
"%s| %s: INFO: Setting replay cache type to %s\n",
523 rcache_dir_env = (
char *)
xmalloc(strlen(
"KRB5RCACHEDIR=")+strlen(rcache_dir)+1);
524 strcpy(rcache_dir_env,
"KRB5RCACHEDIR=");
525 strcat(rcache_dir_env, rcache_dir);
526 putenv(rcache_dir_env);
527 debug((
char *)
"%s| %s: INFO: Setting replay cache directory to %s\n",
532 keytab_name_env = (
char *)
xmalloc(strlen(
"KRB5_KTNAME=")+strlen(keytab_name)+1);
533 strcpy(keytab_name_env,
"KRB5_KTNAME=");
534 strcat(keytab_name_env, keytab_name);
535 putenv(keytab_name_env);
537 keytab_name_env = getenv(
"KRB5_KTNAME");
538 if (!keytab_name_env) {
539 ret = krb5_init_context(&context);
541 krb5_kt_default_name(context, default_keytab,
MAXPATHLEN);
543 keytab_name =
xstrdup(default_keytab);
544 krb5_free_context(context);
546 keytab_name =
xstrdup(keytab_name_env);
549 #if HAVE_KRB5_MEMORY_KEYTAB
550 ret = krb5_init_context(&context);
552 memory_keytab_name = (
char *)
xmalloc(strlen(
"MEMORY:negotiate_kerberos_auth_")+16);
553 snprintf(memory_keytab_name, strlen(
"MEMORY:negotiate_kerberos_auth_")+16,
554 "MEMORY:negotiate_kerberos_auth_%d", (
unsigned int) getpid());
555 ret = krb5_read_keytab(context, keytab_name, &ktlist);
557 debug((
char *)
"%s| %s: ERROR: Reading keytab %s into list failed\n",
560 ret = krb5_write_keytab(context, ktlist, memory_keytab_name);
562 debug((
char *)
"%s| %s: ERROR: Writing list into keytab %s\n",
565 memory_keytab_name_env = (
char *)
xmalloc(strlen(
"KRB5_KTNAME=")+strlen(memory_keytab_name)+1);
566 strcpy(memory_keytab_name_env,
"KRB5_KTNAME=");
567 strcat(memory_keytab_name_env, memory_keytab_name);
568 putenv(memory_keytab_name_env);
570 keytab_name =
xstrdup(memory_keytab_name);
571 debug((
char *)
"%s| %s: INFO: Changed keytab to %s\n",
575 ret = krb5_free_kt_list(context,ktlist);
577 debug((
char *)
"%s| %s: ERROR: Freeing list failed\n",
581 krb5_free_context(context);
583 #ifdef HAVE_HEIMDAL_KERBEROS
584 gsskrb5_register_acceptor_identity(keytab_name);
587 if (fgets(buf,
sizeof(buf) - 1, stdin) ==
nullptr) {
589 debug((
char *)
"%s| %s: FATAL: fgets() failed! dying..... errno=%d (%s)\n",
593 fprintf(stdout,
"BH input error\n");
596 fprintf(stdout,
"BH input error\n");
599 c = (
char *) memchr(buf,
'\n',
sizeof(buf) - 1);
608 fprintf(stdout,
"BH Oversized message\n");
612 debug((
char *)
"%s| %s: DEBUG: Got '%s' from squid (length: %ld).\n",
LogTime(),
PROGRAM, buf, length);
614 if (buf[0] ==
'\0') {
616 fprintf(stdout,
"BH Invalid request\n");
619 if (strlen(buf) < 2) {
621 fprintf(stdout,
"BH Invalid request\n");
624 if (!strncmp(buf,
"QQ", 2)) {
625 gss_release_buffer(&minor_status, &input_token);
626 gss_release_buffer(&minor_status, &output_token);
627 gss_release_buffer(&minor_status, &service);
628 gss_release_cred(&minor_status, &server_creds);
630 gss_release_name(&minor_status, &server_name);
632 gss_release_name(&minor_status, &client_name);
633 if (gss_context != GSS_C_NO_CONTEXT)
634 gss_delete_sec_context(&minor_status, &gss_context,
nullptr);
638 xfree(kerberosToken);
646 xfree(rcache_type_env);
648 xfree(rcache_dir_env);
650 xfree(keytab_name_env);
651 #if HAVE_KRB5_MEMORY_KEYTAB
652 krb5_kt_close(context, memory_keytab);
653 xfree(memory_keytab_name);
654 xfree(memory_keytab_name_env);
657 fprintf(stdout,
"BH quit command\n");
660 if (strncmp(buf,
"YR", 2) && strncmp(buf,
"KK", 2)) {
662 fprintf(stdout,
"BH Invalid request\n");
665 if (!strncmp(buf,
"YR", 2)) {
666 if (gss_context != GSS_C_NO_CONTEXT)
667 gss_delete_sec_context(&minor_status, &gss_context,
nullptr);
668 gss_context = GSS_C_NO_CONTEXT;
670 if (strlen(buf) <= 3) {
671 debug((
char *)
"%s| %s: ERROR: Invalid negotiate request [%s]\n",
LogTime(),
PROGRAM, buf);
672 fprintf(stdout,
"BH Invalid negotiate request\n");
675 const char *b64Token = buf+3;
676 const size_t srcLen = strlen(buf+3);
678 debug((
char *)
"%s| %s: DEBUG: Decode '%s' (decoded length estimate: %d).\n",
680 input_token.value =
xmalloc(input_token.length);
685 if (!
base64_decode_update(&ctx, &dstLen,
static_cast<uint8_t*
>(input_token.value), srcLen, b64Token) ||
687 debug((
char *)
"%s| %s: ERROR: Invalid base64 token [%s]\n",
LogTime(),
PROGRAM, b64Token);
688 fprintf(stdout,
"BH Invalid negotiate request token\n");
691 input_token.length = dstLen;
695 debug((
char *)
"%s| %s: WARNING: received type %d NTLM token\n",
697 (
int) *((
unsigned char *) input_token.value +
699 fprintf(stdout,
"BH received type %d NTLM token\n",
700 (
int) *((
unsigned char *) input_token.value +
704 if (service_principal) {
705 if (strcasecmp(service_principal,
"GSS_C_NO_NAME")) {
706 major_status = gss_import_name(&minor_status, &service,
707 (gss_OID) GSS_C_NULL_OID, &server_name);
710 server_name = GSS_C_NO_NAME;
711 major_status = GSS_S_COMPLETE;
715 major_status = gss_import_name(&minor_status, &service,
723 gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE,
724 GSS_C_NO_OID_SET, GSS_C_ACCEPT, &server_creds,
nullptr,
nullptr);
725 if (
check_gss_err(major_status, minor_status,
"gss_acquire_cred()",
log, 1))
728 major_status = gss_accept_sec_context(&minor_status,
732 GSS_C_NO_CHANNEL_BINDINGS,
733 &client_name,
nullptr, &output_token, &ret_flags,
nullptr,
nullptr);
735 if (output_token.length) {
736 spnegoToken = (
const unsigned char *) output_token.value;
737 spnegoTokenLength = output_token.length;
739 if (token ==
nullptr) {
741 fprintf(stdout,
"BH Not enough memory\n");
746 size_t blen =
base64_encode_update(&tokCtx, token, spnegoTokenLength,
reinterpret_cast<const uint8_t*
>(spnegoToken));
750 if (
check_gss_err(major_status, minor_status,
"gss_accept_sec_context()",
log, 1))
752 if (major_status & GSS_S_CONTINUE_NEEDED) {
754 fprintf(stdout,
"TT token=%s\n", token);
757 gss_release_buffer(&minor_status, &output_token);
759 gss_display_name(&minor_status, client_name, &output_token,
762 if (
check_gss_err(major_status, minor_status,
"gss_display_name()",
log, 1))
764 user = (
char *)
xmalloc(output_token.length + 1);
765 if (user ==
nullptr) {
767 fprintf(stdout,
"BH Not enough memory\n");
770 memcpy(user, output_token.value, output_token.length);
771 user[output_token.length] =
'\0';
772 if (norealm && (p = strchr(user,
'@')) !=
nullptr) {
777 ret = krb5_init_context(&context);
780 #define ADWIN2KPAC 128
781 major_status = gsskrb5_extract_authz_data_from_sec_context(&minor_status,
782 gss_context, ADWIN2KPAC, &data_set);
784 "gsskrb5_extract_authz_data_from_sec_context()",
log, 0)) {
785 ret = krb5_pac_parse(context, data_set.value, data_set.length, &pac);
786 gss_release_buffer(&minor_status, &data_set);
788 ag = get_ad_groups((
char *)&ad_groups, context, pac);
789 krb5_pac_free(context, pac);
791 krb5_free_context(context);
794 type_id.value = (
void *)
"mspac";
795 type_id.length = strlen((
char *)type_id.value);
796 #define KRB5PACLOGONINFO 1
797 major_status = gss_map_name_to_any(&minor_status, client_name, KRB5PACLOGONINFO, &type_id, (gss_any_t *)&pac);
798 if (!
check_gss_err(major_status, minor_status,
"gss_map_name_to_any()",
log, 0)) {
799 ag = get_ad_groups((
char *)&ad_groups,context, pac);
801 (void)gss_release_any_name_mapping(&minor_status, client_name, &type_id, (gss_any_t *)&pac);
802 krb5_free_context(context);
811 fprintf(stdout,
"OK token=%s user=%s %s\n", token, rfc_user, ag?ag:
"group=");
813 fprintf(stdout,
"OK token=%s user=%s\n", token, rfc_user);
815 debug((
char *)
"%s| %s: DEBUG: OK token=%s user=%s\n",
LogTime(),
PROGRAM, token, rfc_user);
817 fprintf(stderr,
"%s| %s: INFO: User %s authenticated\n",
LogTime(),
821 if (
check_gss_err(major_status, minor_status,
"gss_accept_sec_context()",
log, 1))
823 if (major_status & GSS_S_CONTINUE_NEEDED) {
826 fprintf(stdout,
"ERR\n");
829 gss_release_buffer(&minor_status, &output_token);
831 gss_display_name(&minor_status, client_name, &output_token,
834 if (
check_gss_err(major_status, minor_status,
"gss_display_name()",
log, 1))
839 user = (
char *)
xmalloc(output_token.length + 1);
840 if (user ==
nullptr) {
842 fprintf(stdout,
"BH Not enough memory\n");
845 memcpy(user, output_token.value, output_token.length);
846 user[output_token.length] =
'\0';
847 if (norealm && (p = strchr(user,
'@')) !=
nullptr) {
852 fprintf(stdout,
"OK token=%s user=%s %s\n",
"AA==", rfc_user, ag?ag:
"group=");
854 fprintf(stdout,
"OK token=%s user=%s\n",
"AA==", rfc_user);
856 debug((
char *)
"%s| %s: DEBUG: OK token=%s user=%s\n",
LogTime(),
PROGRAM,
"AA==", rfc_user);
858 fprintf(stderr,
"%s| %s: INFO: User %s authenticated\n",
LogTime(),
862 gss_release_buffer(&minor_status, &input_token);
863 gss_release_buffer(&minor_status, &output_token);
864 gss_release_cred(&minor_status, &server_creds);
866 gss_release_name(&minor_status, &server_name);
868 gss_release_name(&minor_status, &client_name);
886 #ifndef MAX_AUTHTOKEN_LEN
887 #define MAX_AUTHTOKEN_LEN 65535
890 main(
int argc,
char *
const argv[])
892 setbuf(stdout,
NULL);
896 if (fgets(buf,
sizeof(buf) - 1, stdin) ==
NULL) {
897 fprintf(stdout,
"BH input error\n");
900 fprintf(stdout,
"BH Kerberos authentication not supported\n");
void log(char *format,...)
const char * LogTime(void)
void base64_encode_init(struct base64_encode_ctx *ctx)
size_t base64_encode_update(struct base64_encode_ctx *ctx, char *dst, size_t length, const uint8_t *src)
void base64_decode_init(struct base64_decode_ctx *ctx)
size_t base64_encode_final(struct base64_encode_ctx *ctx, char *dst)
#define base64_encode_len(length)
int base64_decode_update(struct base64_decode_ctx *ctx, size_t *dst_length, uint8_t *dst, size_t src_length, const char *src)
int base64_decode_final(struct base64_decode_ctx *ctx)
#define BASE64_DECODE_LENGTH(length)
int getopt(int nargc, char *const *nargv, const char *ostr)
#define gss_nt_service_name
char * gethost_name(void)
#define SQUID_KERB_AUTH_VERSION
int check_gss_err(OM_uint32 major_status, OM_uint32 minor_status, const char *function, int log, int sout)
int check_k5_err(krb5_context context, const char *msg, krb5_error_code code)
static const unsigned char ntlmProtocol[]
#define MAX_AUTHTOKEN_LEN
int main(int argc, char *const argv[])
const char * gai_strerror()
#define rfc1738_escape(x)
struct sockaddr * ai_addr
struct addrinfo * ai_next
void * xcalloc(size_t n, size_t sz)