OSDN Git Service

2013.10.24
[uclinux-h8/uClinux-dist.git] / freeswan / pluto / whack.c
1 /* command interface to Pluto
2  * Copyright (C) 1997 Angelos D. Keromytis.
3  * Copyright (C) 1998-2001  D. Hugh Redelmeier.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13  * for more details.
14  *
15  * RCSID $Id: whack.c,v 1.91 2002/03/19 07:17:01 dhr Exp $
16  */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stddef.h>
21 #include <string.h>
22 #include <ctype.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <getopt.h>
31 #include <assert.h>
32
33 #include <freeswan.h>
34
35 #include "constants.h"
36 #include "defs.h"
37 #include "whack.h"
38
39 static void
40 help(void)
41 {
42     fprintf(stderr,
43         "Usage:\n\n"
44         "all forms:"
45             " [--optionsfrom <filename>]"
46             " [--ctlbase <path>]"
47             " [--label <string>]"
48             "\n\n"
49         "help: whack"
50             " [--help]"
51             " [--version]"
52             "\n\n"
53         "connection: whack"
54             " --name <connection_name>"
55             " \\\n   "
56             " [--ipv4 | --ipv6 ]"
57             " [--tunnelipv4 | --tunnelipv6 ]"
58             " \\\n   "
59             " (--host <ip-address> | --id <identity> | --cert <path>)"
60             " [--ikeport <port-number>]"
61             " \\\n   "
62             "   "
63             " [--nexthop <ip-address>]"
64             " [--client <subnet>] | [--clientwithin <address range>])"
65             " [--clientprotoport <protocol>/<port>]"
66             " [--updown <updown>]"
67             " --to"
68             " (--host <ip-address> | --id <identity> | --cert <path>)"
69             " [--ikeport <port-number>]"
70             " \\\n   "
71             "   "
72             " [--nexthop <ip-address>]"
73             " [--client <subnet>] | [--clientwithin <address range>])"
74             " [--clientprotoport <protocol>/<port>]"
75             " [--updown <updown>]"
76             " [--aggrmode]"
77             " [--psk]"
78             " [--rsasig]"
79             " \\\n   "
80             " [--encrypt]"
81             " [--authenticate]"
82             " [--compress]"
83             " [--tunnel]"
84             " [--pfs]"
85             " \\\n   "
86             " [--ikelifetime <seconds>]"
87             " [--ipseclifetime <seconds>]"
88             " \\\n   "
89             " [--reykeymargin <seconds>]"
90             " [--reykeyfuzz <percentage>]"
91             " \\\n   "
92             " [--keyingtries <count>]"
93             " \\\n   "
94             " [--esp <esp-algos>]"
95             " \\\n   "
96             " [--dpddelay <seconds> --dpdtimeout <seconds>]"
97             " [--cipher_p1 <Phase 1 encryption algorithm> --dhg_p1 <Phase 1 Diffie Helman group>]"
98             " [--hash_p1 <Phase 1 hash algorithm>]"
99             " [--dontrekey]"
100             "\n\n"
101         "routing: whack"
102             " (--route | --unroute)"
103             " --name <connection_name>"
104             "\n\n"
105         "initiation: whack"
106             " (--initiate | --terminate)"
107             " --name <connection_name>"
108             " [--asynchronous]"
109             "\n\n"
110         "opportunistic initiation:"
111             "\n"
112             " whack"
113             " [--ipv4 | --ipv6 ]"
114             " [--tunnelipv4 | --tunnelipv6 ]"
115             " \\\n   "
116             " --oppohere <ip-address>"
117             " --oppothere <ip-address>"
118             "\n\n"
119         "delete: whack"
120             " --delete"
121             " --name <connection_name>"
122             "\n\n"
123         "deletestate: whack"
124             " --deletestate <state_object_number>"
125             "\n\n"
126         "pubkey: whack"
127             " --keyid <id>"
128             " [--addkey]"
129             " [--pubkeyrsa <key>]"
130             "\n\n"
131 #ifdef DEBUG
132         "debug: whack [--name <connection_name>]"
133             " \\\n   "
134             " [--debug-none]"
135             " [--debug-all]"
136             " \\\n   "
137             " [--debug-raw]"
138             " [--debug-crypt]"
139             " [--debug-parsing]"
140             " [--debug-emitting]"
141             " \\\n   "
142             " [--debug-control]"
143             " [--debug-klips]"
144             " [--debug-dns]"
145             " [--debug-private]"
146             "\n\n"
147 #endif
148         "listen: whack"
149             " (--listen | --unlisten)"
150             "\n\n"
151         "list: whack [--utc]"
152             " [--listpubkeys]"
153             " [--listcerts]"
154             " [--listcacerts]"
155             " [--listcrls]"
156             " [--listall]"
157             "\n\n"
158         "reread: whack"
159             " [--rereadsecrets]"
160             " [--rereadmycert]"
161             " [--rereadcacerts]"
162             " [--rereadcrls]"
163             " [--rereadall]"
164             "\n\n"
165         "status: whack"
166             " --status"
167             "\n\n"
168         "shutdown: whack"
169             " --shutdown"
170             "\n\n"
171         "FreeS/WAN %s\n",
172         ipsec_version_code());
173 }
174
175 static const char *label = NULL;        /* --label operand, saved for diagnostics */
176
177 static const char *name = NULL; /* --name operand, saved for diagnostics */
178
179 /* print a string as a diagnostic, then exit whack unhappily */
180 static void
181 diag(const char *mess)
182 {
183     if (mess != NULL)
184     {
185         fprintf(stderr, "whack error: ");
186         if (label != NULL)
187             fprintf(stderr, "%s ", label);
188         if (name != NULL)
189             fprintf(stderr, "\"%s\" ", name);
190         fprintf(stderr, "%s\n", mess);
191     }
192
193     exit(RC_WHACK_PROBLEM);
194 }
195
196 /* conditially calls diag; prints second arg, if non-NULL, as quoted string */
197 static void
198 diagq(err_t ugh, const char *this)
199 {
200     if (ugh != NULL)
201     {
202         if (this == NULL)
203         {
204             diag(ugh);
205         }
206         else
207         {
208             char buf[120];      /* arbitrary limit */
209
210             snprintf(buf, sizeof(buf), "%s \"%s\"", ugh, this);
211             diag(buf);
212         }
213     }
214 }
215
216 /* complex combined operands return one of these enumerated values
217  * Note: these become flags in an lset_t.  Since there are more than
218  * 32, we partition them into:
219  * - OPT_* options (most random options)
220  * - DBGOPT_* option (DEBUG options)
221  * - CD_* options (Connection Description options)
222  */
223 enum {
224     OPT_NAME,
225
226     OPT_CD,
227
228     OPT_KEYID,
229     OPT_ADDKEY,
230     OPT_PUBKEYRSA,
231
232     OPT_ROUTE,
233     OPT_UNROUTE,
234     
235  
236     /* put all options that don't require opts_seen service at end */
237     OPT_INITIATE,
238     OPT_TERMINATE,
239     OPT_DELETE,
240     OPT_DELETESTATE,
241     OPT_LISTEN,
242     OPT_UNLISTEN,
243     OPT_UTC,
244     OPT_LISTPUBKEYS,
245     OPT_LISTCERTS,
246     OPT_LISTCACERTS,
247     OPT_LISTCRLS,
248     OPT_LISTALL,
249
250     OPT_REREADSECRETS,
251     OPT_REREADMYCERT,
252     OPT_REREADCACERTS,
253     OPT_REREADCRLS,
254     OPT_REREADALL,
255
256     OPT_STATUS,
257     OPT_SHUTDOWN,
258
259     OPT_OPPO_HERE,
260     OPT_OPPO_THERE,
261
262     OPT_CTLBASE,
263     OPT_ASYNC,
264
265 #   define OPT_LAST OPT_ASYNC   /* last "normal" option */
266
267 /* Connection Description options -- segregated */
268
269 #   define CD_FIRST CD_TO       /* first connection description */
270     CD_TO,
271     CD_HOST,    /* first per-end */
272     CD_ID,
273     CD_CERT,
274     CD_IKEPORT,
275     CD_NEXTHOP,
276     CD_CLIENT,
277     CD_CLIENTWITHIN,
278     CD_CLIENTPROTOPORT,
279     CD_UPDOWN,  /* last per-end */
280
281 #   define CD_POLICY_FIRST  CD_AGGRESSIVE
282     CD_AGGRESSIVE, /* same order as POLICY_* */
283     CD_PSK,     /* same order as POLICY_* */
284     CD_RSASIG,  /* same order as POLICY_* */
285     CD_ENCRYPT, /* same order as POLICY_* */
286     CD_AUTHENTICATE,    /* same order as POLICY_* */
287     CD_COMPRESS,        /* same order as POLICY_* */
288     CD_TUNNEL,  /* same order as POLICY_* */
289     CD_PFS,     /* same order as POLICY_* */
290     CD_DISABLEARRIVALCHECK,     /* same order as POLICY_* */
291     CD_FAIL_PASS,       /* same order as POLICY_* */
292     CD_FAIL_DROP,       /* same order as POLICY_* */
293     CD_DONT_REKEY,      /* same order as POLICY_* */
294     CD_TUNNELIPV4,
295     CD_TUNNELIPV6,
296     CD_CONNIPV4,
297     CD_CONNIPV6,
298
299     CD_IKELIFETIME,
300     CD_IPSECLIFETIME,
301     CD_RKMARGIN,
302     CD_RKFUZZ,
303     CD_KTRIES,
304     CD_DPDDELAY,
305     CD_DPDTIMEOUT,
306     CD_CIPHER_P1,
307     CD_DHG_P1,
308     CD_HASH_P1,
309     CD_IKE,
310     CD_PFSGROUP,
311     CD_ESP,
312     CD_RETRANSMIT
313 #   define CD_LAST CD_RETRANSMIT                /* last connection description */
314
315 #ifdef DEBUG    /* must be last so others are less than 32 to fit in lset_t */
316 #   define DBGOPT_FIRST DBGOPT_NONE
317     ,
318     DBGOPT_NONE,
319     DBGOPT_ALL,
320
321     DBGOPT_RAW,         /* same order as DBG_* */
322     DBGOPT_CRYPT,       /* same order as DBG_* */
323     DBGOPT_PARSING,     /* same order as DBG_* */
324     DBGOPT_EMITTING,    /* same order as DBG_* */
325     DBGOPT_CONTROL,     /* same order as DBG_* */
326     DBGOPT_LIFECYCLE,   /* same order as DBG_* */
327     DBGOPT_KLIPS,       /* same order as DBG_* */
328     DBGOPT_DNS,         /* same order as DBG_* */
329     DBGOPT_PRIVATE      /* same order as DBG_* */
330 #   define DBGOPT_LAST DBGOPT_PRIVATE
331 #endif
332
333 };
334
335 #define OPTION_OFFSET   256     /* to get out of the way of letter options */
336 #define NUMERIC_ARG (1 << 7)    /* expect a numeric argument */
337
338 static const struct option long_opts[] = {
339 #   define OO   OPTION_OFFSET
340     /* name, has_arg, flag, val */
341
342     { "help", no_argument, NULL, 'h' },
343     { "version", no_argument, NULL, 'v' },
344     { "optionsfrom", required_argument, NULL, '+' },
345     { "label", required_argument, NULL, 'l' },
346
347     { "ctlbase", required_argument, NULL, OPT_CTLBASE + OO },
348     { "name", required_argument, NULL, OPT_NAME + OO },
349
350     { "keyid", required_argument, NULL, OPT_KEYID + OO },
351     { "addkey", no_argument, NULL, OPT_ADDKEY + OO },
352     { "pubkeyrsa", required_argument, NULL, OPT_PUBKEYRSA + OO },
353
354     { "route", no_argument, NULL, OPT_ROUTE + OO },
355     { "unroute", no_argument, NULL, OPT_UNROUTE + OO },
356
357     { "initiate", no_argument, NULL, OPT_INITIATE + OO },
358     { "terminate", no_argument, NULL, OPT_TERMINATE + OO },
359     { "delete", no_argument, NULL, OPT_DELETE + OO },
360     { "deletestate", required_argument, NULL, OPT_DELETESTATE + OO + NUMERIC_ARG },
361     { "listen", no_argument, NULL, OPT_LISTEN + OO },
362     { "unlisten", no_argument, NULL, OPT_UNLISTEN + OO },
363     { "utc", no_argument, NULL, OPT_UTC + OO },
364     { "listpubkeys", no_argument, NULL, OPT_LISTPUBKEYS + OO },
365     { "listcerts", no_argument, NULL, OPT_LISTCERTS + OO },
366     { "listcacerts", no_argument, NULL, OPT_LISTCACERTS + OO },
367     { "listcrls", no_argument, NULL, OPT_LISTCRLS + OO },
368     { "listall", no_argument, NULL, OPT_LISTALL + OO },
369     { "rereadsecrets", no_argument, NULL, OPT_REREADSECRETS + OO },
370     { "rereadmycert", no_argument, NULL, OPT_REREADMYCERT + OO },
371     { "rereadcacerts", no_argument, NULL, OPT_REREADCACERTS + OO },
372     { "rereadcrls", no_argument, NULL, OPT_REREADCRLS + OO },
373     { "rereadall", no_argument, NULL, OPT_REREADALL + OO },
374     { "status", no_argument, NULL, OPT_STATUS + OO },
375     { "shutdown", no_argument, NULL, OPT_SHUTDOWN + OO },
376
377     { "oppohere", required_argument, NULL, OPT_OPPO_HERE + OO },
378     { "oppothere", required_argument, NULL, OPT_OPPO_THERE + OO },
379
380     { "asynchronous", no_argument, NULL, OPT_ASYNC + OO },
381
382
383     /* options for a connection description */
384
385     { "to", no_argument, NULL, CD_TO + OO },
386
387     { "host", required_argument, NULL, CD_HOST + OO },
388     { "id", required_argument, NULL, CD_ID + OO },
389     { "cert", required_argument, NULL, CD_CERT + OO },
390     { "ikeport", required_argument, NULL, CD_IKEPORT + OO + NUMERIC_ARG },
391     { "nexthop", required_argument, NULL, CD_NEXTHOP + OO },
392     { "client", required_argument, NULL, CD_CLIENT + OO },
393     { "clientwithin", required_argument, NULL, CD_CLIENTWITHIN + OO },
394     { "clientprotoport", required_argument, NULL, CD_CLIENTPROTOPORT + OO },
395     { "updown", required_argument, NULL, CD_UPDOWN + OO },
396     
397     { "aggrmode", no_argument, NULL, CD_AGGRESSIVE + OO },
398     { "psk", no_argument, NULL, CD_PSK + OO },
399     { "rsasig", no_argument, NULL, CD_RSASIG + OO },
400
401     { "encrypt", no_argument, NULL, CD_ENCRYPT + OO },
402     { "authenticate", no_argument, NULL, CD_AUTHENTICATE + OO },
403     { "compress", no_argument, NULL, CD_COMPRESS + OO },
404     { "tunnel", no_argument, NULL, CD_TUNNEL + OO },
405     { "tunnelipv4", no_argument, NULL, CD_TUNNELIPV4 + OO },
406     { "tunnelipv6", no_argument, NULL, CD_TUNNELIPV6 + OO },
407     { "pfs", no_argument, NULL, CD_PFS + OO },
408     { "disablearrivalcheck", no_argument, NULL, CD_DISABLEARRIVALCHECK + OO },
409     { "pass", no_argument, NULL, CD_FAIL_PASS + OO },
410     { "drop", no_argument, NULL, CD_FAIL_DROP + OO },
411     { "dontrekey", no_argument, NULL, CD_DONT_REKEY + OO },
412     { "ipv4", no_argument, NULL, CD_CONNIPV4 + OO },
413     { "ipv6", no_argument, NULL, CD_CONNIPV6 + OO },
414
415     { "ikelifetime", required_argument, NULL, CD_IKELIFETIME + OO + NUMERIC_ARG },
416     { "ipseclifetime", required_argument, NULL, CD_IPSECLIFETIME + OO + NUMERIC_ARG },
417     { "rekeymargin", required_argument, NULL, CD_RKMARGIN + OO + NUMERIC_ARG },
418     { "rekeywindow", required_argument, NULL, CD_RKMARGIN + OO + NUMERIC_ARG }, /* OBSOLETE */
419     { "rekeyfuzz", required_argument, NULL, CD_RKFUZZ + OO + NUMERIC_ARG },
420     { "keyingtries", required_argument, NULL, CD_KTRIES + OO + NUMERIC_ARG },
421     { "dpddelay", required_argument, NULL, CD_DPDDELAY + OO + NUMERIC_ARG },
422     { "dpdtimeout", required_argument, NULL, CD_DPDTIMEOUT + OO + NUMERIC_ARG },
423     { "cipher_p1", required_argument, NULL, CD_CIPHER_P1 + OO },
424     { "dhg_p1", required_argument, NULL, CD_DHG_P1 + OO + NUMERIC_ARG },
425     { "hash_p1", required_argument, NULL, CD_HASH_P1 + OO },
426     { "ike", required_argument, NULL, CD_IKE + OO },
427     { "pfsgroup", required_argument, NULL, CD_PFSGROUP + OO },
428     { "esp", required_argument, NULL, CD_ESP + OO },
429     { "retransmit_trigger", required_argument, NULL, CD_RETRANSMIT + OO + NUMERIC_ARG},
430 #ifdef DEBUG
431     { "debug-none", no_argument, NULL, DBGOPT_NONE + OO },
432     { "debug-all]", no_argument, NULL, DBGOPT_ALL + OO },
433     { "debug-raw", no_argument, NULL, DBGOPT_RAW + OO },
434     { "debug-crypt", no_argument, NULL, DBGOPT_CRYPT + OO },
435     { "debug-parsing", no_argument, NULL, DBGOPT_PARSING + OO },
436     { "debug-emitting", no_argument, NULL, DBGOPT_EMITTING + OO },
437     { "debug-control", no_argument, NULL, DBGOPT_CONTROL + OO },
438     { "debug-lifecycle", no_argument, NULL, DBGOPT_LIFECYCLE + OO },
439     { "debug-klips", no_argument, NULL, DBGOPT_KLIPS + OO },
440     { "debug-dns", no_argument, NULL, DBGOPT_DNS + OO },
441     { "debug-private", no_argument, NULL, DBGOPT_PRIVATE + OO },
442 #endif
443 #   undef OO
444     { 0,0,0,0 }
445 };
446
447 struct sockaddr_un ctl_addr = { AF_UNIX, DEFAULT_CTLBASE CTL_SUFFIX };
448
449 /* helper variables and function to encode strings from whack message */
450
451 static char
452     *next_str,
453     *str_roof;
454
455 static bool
456 pack_str(char **p)
457 {
458     const char *s = *p == NULL? "" : *p;        /* note: NULL becomes ""! */
459     size_t len = strlen(s) + 1;
460
461     if (str_roof - next_str < (ptrdiff_t)len)
462     {
463         return FALSE;   /* fishy: no end found */
464     }
465     else
466     {
467         strcpy(next_str, s);
468         next_str += len;
469         *p = NULL;      /* don't send pointers on the wire! */
470         return TRUE;
471     }
472 }
473
474 static void
475 check_life_time(time_t life, time_t limit, const char *which
476 , const struct whack_message *msg)
477 {
478     time_t mint = msg->sa_rekey_margin * (100 + msg->sa_rekey_fuzz) / 100;
479
480     if (life > limit)
481     {
482         char buf[200];  /* arbitrary limit */
483
484         snprintf(buf, sizeof(buf)
485             , "%s [%lu seconds] must be less than %lu seconds"
486             , which, (unsigned long)life, (unsigned long)limit);
487         diag(buf);
488     }
489     if ((msg->policy & POLICY_DONT_REKEY) == LEMPTY && life <= mint)
490     {
491         char buf[200];  /* arbitrary limit */
492
493         snprintf(buf, sizeof(buf)
494             , "%s [%lu] must be greater than"
495             " rekeymargin*(100+rekeyfuzz)/100 [%lu*(100+%lu)/100 = %lu]"
496             , which
497             , (unsigned long)life
498             , (unsigned long)msg->sa_rekey_margin
499             , (unsigned long)msg->sa_rekey_fuzz
500             , (unsigned long)mint);
501         diag(buf);
502     }
503 }
504
505 static void
506 check_end(struct whack_end *this, struct whack_end *that
507 , lset_t policy, bool default_nexthop, sa_family_t caf, sa_family_t taf)
508 {
509     if (caf != addrtypeof(&this->host_addr))
510         diag("address family of host inconsistent");
511
512     if (default_nexthop)
513     {
514         if (isanyaddr(&that->host_addr))
515             diag("our nexthop must be specified when other host is a %any or %opportunistic");
516         this->host_nexthop = that->host_addr;
517     }
518
519     if (caf != addrtypeof(&this->host_nexthop))
520         diag("address family of nexthop inconsistent");
521
522     if (this->has_client)
523     {
524         ip_address cn;
525
526         networkof(&this->client, &cn);
527
528         if (taf != addrtypeof(&cn))
529             diag("address family of client subnet inconsistent");
530
531         if (isanyaddr(&cn) && subnetishost(&this->client))
532         {
533             /* client is 0.0.0.0/32: Opportunism connection */
534             if (!isanyaddr(&this->host_addr))
535                 diag("normal client network must not be 0.0.0.0/32 or 0::0/128");
536             if ((policy & (POLICY_PSK | POLICY_RSASIG)) != POLICY_RSASIG)
537                 diag("only RSASIG is supported for opportunism");
538             if ((policy & POLICY_PFS) == 0)
539                 diag("PFS required for opportunism");
540             if ((policy & POLICY_ENCRYPT) == 0)
541                 diag("encryption required for opportunism");
542         }
543     }
544     else
545     {
546         /* fill in anyaddr-anyaddr as (missing) client subnet */
547         ip_address cn;
548
549         diagq(anyaddr(caf, &cn), NULL);
550         diagq(rangetosubnet(&cn, &cn, &this->client), NULL);
551     }
552
553     /* check protocol */
554     if (this->protocol != that->protocol)
555         diagq("the protocol for leftprotoport and rightprotoport must be the same", NULL);
556 }
557
558 /* This is a hack for initiating ISAKMP exchanges. */
559
560 int
561 main(int argc, char **argv)
562 {
563     struct whack_message msg;
564     char esp_buf[256];  /* uses snprintf */
565     lset_t
566         opts_seen = LEMPTY,
567         cd_seen = LEMPTY,
568         cd_seen_before_to;
569     const char
570         *af_used_by = NULL,
571         *tunnel_af_used_by = NULL;
572
573     zero(&msg);
574     msg.magic = WHACK_MAGIC;
575     msg.right.host_port = IKE_UDP_PORT;
576
577     msg.name = NULL;
578     msg.dnshostname = NULL;
579     msg.left.id = NULL;
580     msg.left.cert = NULL;
581     msg.left.updown = NULL;
582     msg.right.id = NULL;
583     msg.right.cert = NULL;
584     msg.right.updown = NULL;
585     msg.keyid = NULL;
586     msg.keyval.ptr = NULL;
587     msg.cipher_p1 = NULL;
588     msg.dhg_p1 = NULL;
589     msg.hash_p1 = NULL;
590     msg.esp = NULL;
591     msg.pfsgroup = NULL;
592     msg.ike = NULL;
593     msg.retransmit_trigger = 2;
594
595     msg.sa_ike_life_seconds = OAKLEY_ISAKMP_SA_LIFETIME_DEFAULT;
596     msg.sa_ipsec_life_seconds = PLUTO_SA_LIFE_DURATION_DEFAULT;
597     msg.sa_rekey_margin = SA_REPLACEMENT_MARGIN_DEFAULT;
598     msg.sa_rekey_fuzz = SA_REPLACEMENT_FUZZ_DEFAULT;
599     msg.sa_keying_tries = SA_REPLACEMENT_RETRIES_DEFAULT;
600
601     msg.addr_family = AF_INET;
602     msg.tunnel_addr_family = AF_INET;
603     
604     for (;;)
605     {
606         int long_index;
607         unsigned long opt_whole;        /* numeric argument for some flags */
608
609         /* Note: we don't like the way short options get parsed
610          * by getopt_long, so we simply pass an empty string as
611          * the list.  It could be "hp:d:c:o:eatfs" "NARXPECK".
612          */
613         int c = getopt_long(argc, argv, "", long_opts, &long_index) - OPTION_OFFSET;
614
615         /* decode a numeric argument, if expected */
616         if (0 <= c && (c & NUMERIC_ARG))
617         {
618             char *endptr;
619
620             c -= NUMERIC_ARG;
621             opt_whole = strtoul(optarg, &endptr, 0);
622
623             if (*endptr != '\0' || endptr == optarg)
624                 diagq("badly formed numeric argument", optarg);
625         }
626
627         /* per-class option processing */
628         if (0 <= c && c < OPT_LAST)
629         {
630             /* OPT_* options get added opts_seen.
631              * Reject repeated options (unless later code intervenes).
632              */
633             lset_t f = LELEM(c);
634
635             if (opts_seen & f)
636                 diagq("duplicated flag", long_opts[long_index].name);
637             opts_seen |= f;
638         }
639 #ifdef DEBUG
640         else if (DBGOPT_FIRST <= c && c <= DBGOPT_LAST)
641         {
642             /* DBGOPT_* options are treated separately to reduce
643              * potential members of opts_seen.
644              */
645             msg.whack_options = TRUE;
646         }
647 #endif
648         else if (CD_FIRST <= c && c <= CD_LAST)
649         {
650             /* CD_* options are added to cd_seen.
651              * Reject repeated options (unless later code intervenes).
652              */
653             lset_t f = LELEM(c - CD_FIRST);
654
655             if ((CD_FIRST+32) <= c)
656                 f = 0;
657             if (cd_seen & f)
658                 diagq("duplicated flag", long_opts[long_index].name);
659             cd_seen |= f;
660             opts_seen |= LELEM(OPT_CD);
661         }
662
663         /* Note: "break"ing from switch terminates loop.
664          * most cases should end with "continue".
665          */
666         switch (c)
667         {
668         case EOF - OPTION_OFFSET:       /* end of flags */
669             break;
670
671         case 0 - OPTION_OFFSET: /* long option already handled */
672             continue;
673
674         case ':' - OPTION_OFFSET:       /* diagnostic already printed by getopt_long */
675         case '?' - OPTION_OFFSET:       /* diagnostic already printed by getopt_long */
676             diag(NULL); /* print no additional diagnostic, but exit sadly */
677             break;      /* not actually reached */
678
679         case 'h' - OPTION_OFFSET:       /* --help */
680             help();
681             return 0;   /* GNU coding standards say to stop here */
682
683         case 'v' - OPTION_OFFSET:       /* --version */
684             {
685                 const char **sp = ipsec_copyright_notice();
686
687                 printf("%s\n", ipsec_version_string());
688                 for (; *sp != NULL; sp++)
689                     puts(*sp);
690             }
691             return 0;   /* GNU coding standards say to stop here */
692
693         case 'l' - OPTION_OFFSET:       /* --label <string> */
694             label = optarg;     /* remember for diagnostics */
695             continue;
696
697         case '+' - OPTION_OFFSET:       /* --optionsfrom <filename> */
698             optionsfrom(optarg, &argc, &argv, optind, stderr);
699             fprintf(stderr, "DAN %s", optarg);
700             /* does not return on error */
701             continue;
702
703         /* the rest of the options combine in complex ways */
704
705         case OPT_CTLBASE:       /* --port <ctlbase> */
706             if (snprintf(ctl_addr.sun_path, sizeof(ctl_addr.sun_path)
707             , "%s%s", optarg, CTL_SUFFIX) == -1)
708                 diag("<ctlbase>" CTL_SUFFIX " must be fit in a sun_addr");
709             continue;
710
711         case OPT_NAME:  /* --name <connection-name> */
712             name = optarg;
713             msg.name = optarg;
714             continue;
715
716         case OPT_KEYID: /* --keyid <identity> */
717             msg.whack_key = TRUE;
718             msg.keyid = optarg; /* decoded by Pluto */
719             continue;
720
721         case OPT_ADDKEY:        /* --addkey */
722             msg.whack_addkey = TRUE;
723             continue;
724
725         case OPT_PUBKEYRSA:     /* --pubkeyrsa <key> */
726             {
727                 static char keyspace[1024 + 4]; /* room for 8K bit key */
728                 char diag_space[TTODATAV_BUF];
729                 const char *ugh = ttodatav(optarg, 0, 0
730                     , keyspace, sizeof(keyspace)
731                     , &msg.keyval.len, diag_space, sizeof(diag_space));
732
733                 if (ugh != NULL)
734                 {
735                     char ugh_space[80]; /* perhaps enough space */
736
737                     snprintf(ugh_space, sizeof(ugh_space)
738                         , "RSA public-key data malformed (%s)", ugh);
739                     diagq(ugh_space, optarg);
740                 }
741                 msg.pubkey_alg = PUBKEY_ALG_RSA;
742                 msg.keyval.ptr = keyspace;
743             }
744             continue;
745
746         case OPT_ROUTE: /* --route */
747             msg.whack_route = TRUE;
748             continue;
749
750         case OPT_UNROUTE:       /* --unroute */
751             msg.whack_unroute = TRUE;
752             continue;
753
754         case OPT_INITIATE:      /* --initiate */
755             msg.whack_initiate = TRUE;
756             continue;
757
758         case OPT_TERMINATE:     /* --terminate */
759             msg.whack_terminate = TRUE;
760             continue;
761
762         case OPT_DELETE:        /* --delete */
763             msg.whack_delete = TRUE;
764             continue;
765
766         case OPT_DELETESTATE:   /* --deletestate <state_object_number> */
767             msg.whack_deletestate = TRUE;
768             msg.whack_deletestateno = opt_whole;
769             continue;
770
771         case OPT_LISTEN:        /* --listen */
772             msg.whack_listen = TRUE;
773             continue;
774
775         case OPT_UNLISTEN:      /* --unlisten */
776             msg.whack_unlisten = TRUE;
777             continue;
778
779         case OPT_UTC:           /* --utc */
780             msg.whack_utc = TRUE;
781             continue;
782
783         case OPT_LISTPUBKEYS:   /* --listpubkeys */
784         case OPT_LISTCERTS:     /* --listcerts */
785         case OPT_LISTCACERTS:   /* --listcacerts */
786         case OPT_LISTCRLS:      /* --listcrls */
787             msg.whack_list |= LELEM(c-OPT_LISTPUBKEYS);
788             continue;
789
790         case OPT_LISTALL:       /* --listall */
791             msg.whack_list = LIST_ALL;
792             continue;
793
794         case OPT_REREADSECRETS: /* --rereadsecrets */
795         case OPT_REREADMYCERT:  /* --rereadmycert */
796         case OPT_REREADCACERTS: /* --rereadcacerts */
797         case OPT_REREADCRLS:    /* --rereadcrls */
798             msg.whack_reread |= LELEM(c-OPT_REREADSECRETS);
799             continue;
800
801         case OPT_REREADALL:     /* --rereadall */
802             msg.whack_reread = REREAD_ALL;
803             continue;
804
805         case OPT_STATUS:        /* --status */
806             msg.whack_status = TRUE;
807             continue;
808
809         case OPT_SHUTDOWN:      /* --shutdown */
810             msg.whack_shutdown = TRUE;
811             continue;
812
813         case OPT_OPPO_HERE:     /* --oppohere <ip-address> */
814             af_used_by = long_opts[long_index].name;
815             diagq(ttoaddr(optarg, 0, msg.addr_family, &msg.oppo_my_client), optarg);
816             if (isanyaddr(&msg.oppo_my_client))
817                 diagq("0.0.0.0 or 0::0 isn't a valid client address", optarg);
818             continue;
819
820         case OPT_OPPO_THERE:    /* --oppohere <ip-address> */
821             af_used_by = long_opts[long_index].name;
822             diagq(ttoaddr(optarg, 0, msg.addr_family, &msg.oppo_peer_client), optarg);
823             if (isanyaddr(&msg.oppo_peer_client))
824                 diagq("0.0.0.0 or 0::0 isn't a valid client address", optarg);
825             continue;
826
827         case OPT_ASYNC:
828             msg.whack_async = TRUE;
829             continue;
830
831
832         /* Connection Description options */
833
834         case CD_HOST:   /* --host <ip-address> */
835             af_used_by = long_opts[long_index].name;
836             if (streq(optarg, "%any") || streq(optarg, "0.0.0.0"))
837             {
838                 diagq(anyaddr(msg.addr_family, &msg.right.host_addr), optarg);
839             }
840             else if (streq(optarg, "%opportunistic"))
841             {
842                 /* we also fill in --client to 0.0.0.0/32 or IPV6 equivalent */
843                 ip_address  any;
844
845                 if (cd_seen & LELEM(CD_CLIENT - CD_FIRST))
846                     diag("%opportunistic clashes with --client");
847
848                 cd_seen |= LELEM(CD_CLIENT - CD_FIRST);
849                 tunnel_af_used_by = optarg;
850                 diagq(anyaddr(msg.addr_family, &msg.right.host_addr), optarg);
851                 diagq(anyaddr(msg.tunnel_addr_family, &any), optarg);
852                 diagq(rangetosubnet(&any, &any, &msg.right.client), optarg);
853                 msg.right.has_client = TRUE;
854                 /* always use tunnel mode; mark as opportunistic */
855                 msg.policy |= POLICY_TUNNEL | POLICY_OPPO;
856             }
857             else
858             {
859                 if (msg.left.id != NULL) {
860                         msg.dnshostname = optarg;
861                 }
862                 diagq(ttoaddr(optarg, 0, msg.addr_family
863                     , &msg.right.host_addr), optarg);
864             }
865             continue;
866
867         case CD_ID:     /* --id <identity> */
868             msg.right.id = optarg;      /* decoded by Pluto */
869             continue;
870
871         case CD_CERT:   /* --cert <path> */
872             msg.right.cert = optarg;    /* decoded by Pluto */
873             continue;
874
875         case CD_IKEPORT:        /* --ikeport <port-number> */
876             if (opt_whole<=0 || opt_whole >= 0x10000)
877                 diagq("<port-number> must be a number between 1 and 65535", optarg);
878             msg.right.host_port = opt_whole;
879             continue;
880
881         case CD_NEXTHOP:        /* --nexthop <ip-address> */
882             af_used_by = long_opts[long_index].name;
883             if (streq(optarg, "%direct"))
884                 diagq(anyaddr(msg.addr_family
885                     , &msg.right.host_nexthop), optarg);
886             else
887                 diagq(ttoaddr(optarg, 0, msg.addr_family
888                     , &msg.right.host_nexthop), optarg);
889             continue;
890
891         case CD_CLIENT: /* --client <subnet> */
892             if (cd_seen & LELEM(CD_CLIENTWITHIN - CD_FIRST))
893                 diag("--client conflicts with --clientwithin");
894             tunnel_af_used_by = long_opts[long_index].name;
895 #ifdef VIRTUAL_IP
896             if ( ((strlen(optarg)>=6) && (strncmp(optarg,"vhost:",6)==0)) ||
897                 ((strlen(optarg)>=5) && (strncmp(optarg,"vnet:",5)==0)) ) {
898                 msg.right.virt = optarg;
899             }
900             else {
901                 diagq(ttosubnet(optarg, 0, msg.tunnel_addr_family, &msg.right.client), optarg);
902                 msg.right.has_client = TRUE;
903             }
904 #else
905             diagq(ttosubnet(optarg, 0, msg.tunnel_addr_family, &msg.right.client), optarg);
906             msg.right.has_client = TRUE;
907 #endif
908             msg.policy |= POLICY_TUNNEL;        /* client => tunnel */
909             continue;
910
911         case CD_CLIENTWITHIN:   /* --clienwithin <address range> */
912             if (cd_seen & LELEM(CD_CLIENT - CD_FIRST))
913                 diag("--clientwithin conflicts with --client");
914             tunnel_af_used_by = long_opts[long_index].name;
915             diagq(ttosubnet(optarg, 0, msg.tunnel_addr_family, &msg.right.client), optarg);
916             msg.right.has_client = TRUE;
917             msg.policy |= POLICY_TUNNEL;        /* client => tunnel */
918             msg.right.has_client_wildcard = TRUE;
919             continue;
920
921         case CD_CLIENTPROTOPORT: /* --clientprotoport <protocol>/<port> */
922             diagq(ttoprotoport(optarg, 0, &msg.right.protocol,
923                 &msg.right.port), optarg);
924             continue;
925
926         case CD_UPDOWN: /* --updown <updown> */
927             msg.right.updown = optarg;
928             continue;
929
930         case CD_TO:             /* --to */
931             /* process right end, move it to left, reset it */
932             if ((cd_seen & LELEM(CD_HOST-CD_FIRST)) == 0)
933                 diag("connection missing --host before --to");
934             msg.left = msg.right;
935             zero(&msg.right);
936             msg.right.id = NULL;
937             msg.right.cert = NULL;
938             msg.right.updown = NULL;
939             msg.right.host_port = IKE_UDP_PORT;
940             cd_seen_before_to = cd_seen;
941             cd_seen &= ~LRANGE(CD_HOST-CD_FIRST, CD_UPDOWN-CD_FIRST);
942             continue;
943
944         case CD_AGGRESSIVE:     /* --aggrmode */
945         case CD_PSK:            /* --psk */
946         case CD_RSASIG:         /* --rsasig */
947         case CD_ENCRYPT:        /* --encrypt */
948         case CD_AUTHENTICATE:   /* --authenticate */
949         case CD_COMPRESS:       /* --compress */
950         case CD_TUNNEL:         /* --tunnel */
951         case CD_PFS:            /* --pfs */
952         case CD_DISABLEARRIVALCHECK:    /* --disablearrivalcheck */
953         case CD_FAIL_PASS:      /* --pass */
954         case CD_FAIL_DROP:      /* --drop */
955         case CD_DONT_REKEY:     /* --donotrekey */
956             msg.policy |= LELEM(c - CD_POLICY_FIRST);
957             continue;
958
959         case CD_IKELIFETIME:    /* --ikelifetime <seconds> */
960             msg.sa_ike_life_seconds = opt_whole;
961             continue;
962
963         case CD_IPSECLIFETIME:  /* --ipseclifetime <seconds> */
964             msg.sa_ipsec_life_seconds = opt_whole;
965             continue;
966
967         case CD_RKMARGIN:       /* --rekeymargin <seconds> */
968             msg.sa_rekey_margin = opt_whole;
969             continue;
970
971         case CD_RKFUZZ: /* --rekeyfuzz <percentage> */
972             msg.sa_rekey_fuzz = opt_whole;
973             continue;
974
975         case CD_KTRIES: /* --keyingtries <count> */
976             msg.sa_keying_tries = opt_whole;
977             continue;
978
979         case CD_DPDDELAY:       /* --dpddelay */
980             msg.dpd_delay = opt_whole;
981             continue;
982
983         case CD_DPDTIMEOUT:     /* --dpdtimeout */
984             msg.dpd_timeout = opt_whole;
985             continue;
986
987         case CD_CIPHER_P1:      /* --cipher */
988             if (streq(optarg, "des"))
989                 msg.cipher_p1 = OAKLEY_DES_CBC;
990             else if (streq(optarg, "3des"))
991                 msg.cipher_p1 = OAKLEY_3DES_CBC;
992             continue;
993
994         case CD_DHG_P1: /* --dhg */
995             msg.dhg_p1 = opt_whole;
996             continue;
997         
998         case CD_HASH_P1:        /* --hash */
999             if (streq(optarg, "md5"))
1000                 msg.hash_p1 = OAKLEY_MD5;
1001             else if (streq(optarg, "sha"))
1002                 msg.hash_p1 = OAKLEY_SHA;
1003             continue;
1004
1005         case CD_IKE:    /* --ike <ike_alg1,ike_alg2,...> */
1006             msg.ike = optarg;
1007             continue;
1008             
1009         case CD_PFSGROUP:       /* --pfsgroup modpXXXX */
1010             msg.pfsgroup = optarg;
1011             continue;
1012
1013         case CD_ESP:    /* --esp <esp_alg1,esp_alg2,...> */
1014             msg.esp = optarg;
1015             continue;
1016
1017         case CD_RETRANSMIT:
1018             msg.retransmit_trigger = opt_whole;
1019             continue;
1020
1021         case CD_CONNIPV4:
1022             if (cd_seen & LELEM(CD_CONNIPV6 - CD_FIRST))
1023                 diag("--ipv4 conflicts with --ipv6");
1024
1025             /* Since this is the default, the flag is redundant.
1026              * So we don't need to set msg.addr_family
1027              * and we don't need to check af_used_by
1028              * and we don't have to consider defaulting tunnel_addr_family.
1029              */
1030             continue;
1031
1032         case CD_CONNIPV6:
1033             if (cd_seen & LELEM(CD_CONNIPV4 - CD_FIRST))
1034                 diag("--ipv6 conflicts with --ipv4");
1035
1036             if (af_used_by != NULL)
1037                 diagq("--ipv6 must precede", af_used_by);
1038
1039             af_used_by = long_opts[long_index].name;
1040             msg.addr_family = AF_INET6;
1041
1042             /* Consider defaulting tunnel_addr_family to AF_INET6.
1043              * Do so only if it hasn't yet been specified or used.
1044              */
1045             if ((cd_seen & (LELEM(CD_TUNNELIPV4 - CD_FIRST) | LELEM(CD_TUNNELIPV6 - CD_FIRST))) == 0
1046             && tunnel_af_used_by == NULL)
1047                 msg.tunnel_addr_family = AF_INET6;
1048             continue;
1049
1050         case CD_TUNNELIPV4:
1051             if (cd_seen & LELEM(CD_TUNNELIPV6 - CD_FIRST))
1052                 diag("--tunnelipv4 conflicts with --tunnelipv6");
1053
1054             if (tunnel_af_used_by != NULL)
1055                 diagq("--tunnelipv4 must precede", af_used_by);
1056
1057             msg.tunnel_addr_family = AF_INET;
1058             continue;
1059
1060         case CD_TUNNELIPV6:
1061             if (cd_seen & LELEM(CD_TUNNELIPV4 - CD_FIRST))
1062                 diag("--tunnelipv6 conflicts with --tunnelipv4");
1063
1064             if (tunnel_af_used_by != NULL)
1065                 diagq("--tunnelipv6 must precede", af_used_by);
1066
1067             msg.tunnel_addr_family = AF_INET6;
1068             continue;
1069
1070 #ifdef DEBUG
1071         case DBGOPT_NONE:       /* --debug-none */
1072             msg.debugging = DBG_NONE;
1073             continue;
1074
1075         case DBGOPT_ALL:        /* --debug-all */
1076             msg.debugging |= DBG_ALL;   /* note: does not include PRIVATE */
1077             continue;
1078
1079         case DBGOPT_RAW:        /* --debug-raw */
1080         case DBGOPT_CRYPT:      /* --debug-crypt */
1081         case DBGOPT_PARSING:    /* --debug-parsing */
1082         case DBGOPT_EMITTING:   /* --debug-emitting */
1083         case DBGOPT_CONTROL:    /* --debug-control */
1084         case DBGOPT_LIFECYCLE:  /* --debug-lifecycle */
1085         case DBGOPT_KLIPS:      /* --debug-klips */
1086         case DBGOPT_DNS:        /* --debug-dns */
1087         case DBGOPT_PRIVATE:    /* --debug-private */
1088             msg.debugging |= LELEM(c-DBGOPT_RAW);
1089             continue;
1090 #endif
1091         default:
1092             assert(FALSE);      /* unknown return value */
1093         }
1094         break;
1095     }
1096
1097     if (optind != argc) {
1098         fprintf(stderr, "optind %d ", optind);
1099         fprintf(stderr, "argc %d ", argc);
1100         fprintf(stderr, "optarg %s ", optarg);
1101         diagq("unexpected argument", argv[optind]);
1102     }
1103     /* For each possible form of the command, figure out if an argument
1104      * suggests whether that form was intended, and if so, whether all
1105      * required information was supplied.
1106      */
1107
1108     /* check opportunistic initiation simulation request */
1109     switch (opts_seen & (LELEM(OPT_OPPO_HERE) | LELEM(OPT_OPPO_THERE)))
1110     {
1111     case LELEM(OPT_OPPO_HERE):
1112     case LELEM(OPT_OPPO_THERE):
1113         diag("--oppohere and --oppothere must be used together");
1114         /*NOTREACHED*/
1115     case LELEM(OPT_OPPO_HERE) | LELEM(OPT_OPPO_THERE):
1116         msg.whack_oppo_initiate = TRUE;
1117         break;
1118     }
1119
1120     /* check connection description */
1121     if (opts_seen & LELEM(OPT_CD))
1122     {
1123         if (!LALLIN(cd_seen, LELEM(CD_TO-CD_FIRST)))
1124             diag("connection description option, but no --to");
1125
1126         if ((cd_seen & LELEM(CD_HOST-CD_FIRST)) == 0)
1127             diag("connection missing --host after --to");
1128
1129         if ((cd_seen & (CD_PSK | CD_RSASIG)) == 0)
1130             diag("connection must have --psk or --rsasig or both");
1131
1132         if (isanyaddr(&msg.left.host_addr)
1133         && isanyaddr(&msg.right.host_addr))
1134             diag("hosts cannot both be 0.0.0.0 or 0::0");
1135
1136         check_end(&msg.left, &msg.right, msg.policy, (cd_seen_before_to & LELEM(CD_NEXTHOP-CD_FIRST)) == 0
1137             , msg.addr_family, msg.tunnel_addr_family);
1138
1139         check_end(&msg.right, &msg.left, msg.policy, (cd_seen & LELEM(CD_NEXTHOP-CD_FIRST)) == 0
1140             , msg.addr_family, msg.tunnel_addr_family);
1141
1142         if (subnettypeof(&msg.left.client) != subnettypeof(&msg.right.client))
1143             diag("endpoints clash: one is IPv4 and the other is IPv6");
1144
1145         if ((msg.policy & POLICY_ID_AUTH_MASK) == LEMPTY)
1146             diag("must specify --rsasig or --psk for a connection");
1147
1148         if (!HAS_IPSEC_POLICY(msg.policy)
1149         && (msg.left.has_client || msg.right.has_client))
1150             diag("must not specify clients for ISAKMP-only connection");
1151
1152         msg.whack_connection = TRUE;
1153     }
1154
1155     /* decide whether --name is mandatory or forbidden */
1156     if (opts_seen & (LELEM(OPT_ROUTE) | LELEM(OPT_UNROUTE)
1157     | LELEM(OPT_INITIATE) | LELEM(OPT_TERMINATE)
1158     | LELEM(OPT_DELETE) | LELEM(OPT_CD)))
1159     {
1160         if ((opts_seen & LELEM(OPT_NAME)) == 0)
1161             diag("missing --name <connection_name>");
1162     }
1163     else if (!msg.whack_options)
1164     {
1165         if ((opts_seen & LELEM(OPT_NAME)) != 0)
1166             diag("no reason for --name");
1167     }
1168
1169     if (opts_seen & (LELEM(OPT_PUBKEYRSA) | LELEM(OPT_ADDKEY)))
1170     {
1171         if ((opts_seen & LELEM(OPT_KEYID)) == 0)
1172             diag("--addkey and --pubkeyrsa require --keyid");
1173     }
1174
1175     if (!(msg.whack_connection || msg.whack_key
1176     || msg.whack_delete || msg.whack_deletestate
1177     || msg.whack_initiate || msg.whack_oppo_initiate || msg.whack_terminate
1178     || msg.whack_route || msg.whack_unroute || msg.whack_listen
1179     || msg.whack_unlisten || msg.whack_list || msg.whack_reread
1180     || msg.whack_status || msg.whack_options || msg.whack_shutdown))
1181     {
1182         diag("no action specified; try --help for hints");
1183     }
1184
1185     /* tricky quick and dirty check for wild values */
1186     if (msg.sa_rekey_margin != 0
1187     && msg.sa_rekey_fuzz * msg.sa_rekey_margin * 4 / msg.sa_rekey_margin / 4
1188      != msg.sa_rekey_fuzz)
1189         diag("rekeymargin or rekeyfuzz values are so large that they cause oveflow");
1190
1191     check_life_time (msg.sa_ike_life_seconds, OAKLEY_ISAKMP_SA_LIFETIME_MAXIMUM
1192         , "ikelifetime", &msg);
1193
1194     check_life_time(msg.sa_ipsec_life_seconds, SA_LIFE_DURATION_MAXIMUM
1195         , "ipseclifetime", &msg);
1196
1197     if (msg.dpd_delay && !msg.dpd_timeout)
1198         diag("dpddelay specified, but dpdtimeout is zero");
1199
1200     /* build esp message as esp="<esp>;<pfsgroup>" */
1201     if (msg.pfsgroup) {
1202             snprintf(esp_buf, sizeof (esp_buf), "%s;%s", 
1203                     msg.esp ? msg.esp : "",
1204                     msg.pfsgroup ? msg.pfsgroup : "");
1205             msg.esp=esp_buf;
1206     }
1207     /* pack strings for inclusion in message */
1208     next_str = msg.string;
1209     str_roof = &msg.string[sizeof(msg.string)];
1210
1211     if (!pack_str(&msg.name)            /* string 1 */
1212     || !pack_str(&msg.left.id)          /* string 2 */
1213     || !pack_str(&msg.left.cert)        /* string 3 */
1214     || !pack_str(&msg.left.updown)      /* string 4 */
1215 #ifdef VIRTUAL_IP
1216     || !pack_str(&msg.left.virt)
1217 #endif
1218     || !pack_str(&msg.right.id)         /* string 5 */
1219     || !pack_str(&msg.right.cert)       /* string 6 */
1220     || !pack_str(&msg.right.updown)     /* string 7 */
1221 #ifdef VIRTUAL_IP
1222     || !pack_str(&msg.right.virt)
1223 #endif
1224     || !pack_str(&msg.keyid)            /* string 8 */
1225     || !pack_str(&msg.ike)              /* string 9 */
1226     || !pack_str(&msg.esp)              /* string 10 */
1227     || !pack_str(&msg.dnshostname)      /* string 11 */
1228     || str_roof - next_str < (ptrdiff_t)msg.keyval.len)    /* chunk (sort of string 5) */
1229         diag("too many bytes of strings to fit in message to pluto");
1230
1231     memcpy(next_str, msg.keyval.ptr, msg.keyval.len);
1232     msg.keyval.ptr = NULL;
1233     next_str += msg.keyval.len;
1234
1235     /* send message to Pluto */
1236     if (access(ctl_addr.sun_path, R_OK | W_OK) < 0)
1237     {
1238         int e = errno;
1239
1240         switch(e)
1241         {
1242         case EACCES:
1243             fprintf(stderr, "whack: no right to communicate with pluto (access(\"%s\"))\n"
1244                 , ctl_addr.sun_path);
1245             break;
1246         case ENOENT:
1247             fprintf(stderr, "whack: Pluto is not running (no \"%s\")\n"
1248                 , ctl_addr.sun_path);
1249             break;
1250         default:
1251             fprintf(stderr, "whack: access(\"%s\") failed with %d %s\n"
1252                 , ctl_addr.sun_path, errno, strerror(e));
1253             break;
1254         }
1255         exit(RC_WHACK_PROBLEM);
1256     }
1257     else
1258     {
1259         int sock = socket(AF_UNIX, SOCK_STREAM, 0);
1260         int exit_status = 0;
1261         ssize_t len = next_str - (char *)&msg;
1262
1263         if (sock == -1)
1264         {
1265             int e = errno;
1266
1267             fprintf(stderr, "whack: socket() failed (%d %s)\n", e, strerror(e));
1268             exit(RC_WHACK_PROBLEM);
1269         }
1270
1271         if (connect(sock, (struct sockaddr *)&ctl_addr
1272         , offsetof(struct sockaddr_un, sun_path) + strlen(ctl_addr.sun_path)) < 0)
1273         {
1274             int e = errno;
1275
1276             fprintf(stderr, "whack:%s connect() for \"%s\" failed (%d %s)\n"
1277                 , e == ECONNREFUSED? " is Pluto running? " : ""
1278                 , ctl_addr.sun_path, e, strerror(e));
1279             exit(RC_WHACK_PROBLEM);
1280         }
1281
1282         if (write(sock, &msg, len) != len)
1283         {
1284             int e = errno;
1285
1286             fprintf(stderr, "whack: write() failed (%d %s)\n", e, strerror(e));
1287             exit(RC_WHACK_PROBLEM);
1288         }
1289
1290         /* for now, just copy reply back to stdout */
1291
1292         {
1293             char buf[4097];     /* arbitrary limit on log line length */
1294             char *be = buf;
1295
1296             for (;;)
1297             {
1298                 char *ls = buf;
1299                 ssize_t rl = read(sock, be, (buf + sizeof(buf)-1) - be);
1300
1301                 if (rl < 0)
1302                 {
1303                     int e = errno;
1304
1305                     fprintf(stderr, "whack: read() failed (%d %s)\n", e, strerror(e));
1306                     exit(RC_WHACK_PROBLEM);
1307                 }
1308                 if (rl == 0)
1309                 {
1310                     if (be != buf)
1311                         fprintf(stderr, "whack: last line from pluto too long or unterminated\n");
1312                     break;
1313                 }
1314
1315                 be += rl;
1316                 *be = '\0';
1317
1318                 for (;;)
1319                 {
1320                     char *le = strchr(ls, '\n');
1321
1322                     if (le == NULL)
1323                     {
1324                         /* move last, partial line to start of buffer */
1325                         memmove(buf, ls, be-ls);
1326                         be -= ls - buf;
1327                         break;
1328                     }
1329
1330                     le++;       /* include NL in line */
1331
1332                     /* figure out prefix number
1333                      * and how it should affect our exit status
1334                      */
1335                     {
1336                         unsigned long s = strtoul(ls, NULL, 10);
1337
1338                         switch (s)
1339                         {
1340                         case RC_COMMENT:
1341                         case RC_LOG:
1342                             /* ignore */
1343                             break;
1344                         case RC_SUCCESS:
1345                             /* be happy */
1346                             exit_status = 0;
1347                             break;
1348                         /* case RC_LOG_SERIOUS: */
1349                         default:
1350                             /* pass through */
1351                             exit_status = s;
1352                             break;
1353                         }
1354                     }
1355                     write(1, ls, le - ls);
1356                     ls = le;
1357                 }
1358             }
1359         }
1360         return exit_status;
1361     }
1362 }