extcap: make extcap use the ws_strtoi/u functions.
[metze/wireshark/wip.git] / extcap / ciscodump.c
1 /* ciscodump.c
2  * ciscodump is extcap tool used to capture data using a ssh on a remote cisco router
3  *
4  * Copyright 2015, Dario Lombardo
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #include "config.h"
26
27 #include <extcap/extcap-base.h>
28 #include <wsutil/interface.h>
29 #include <wsutil/strtoi.h>
30 #include <extcap/ssh-base.h>
31 #include <writecap/pcapio.h>
32
33 #include <errno.h>
34 #include <string.h>
35 #include <fcntl.h>
36
37 #define CISCODUMP_VERSION_MAJOR "1"
38 #define CISCODUMP_VERSION_MINOR "0"
39 #define CISCODUMP_VERSION_RELEASE "0"
40
41 /* The read timeout in msec */
42 #define CISCODUMP_READ_TIMEOUT 3000
43
44 #define CISCODUMP_EXTCAP_INTERFACE "cisco"
45 #define SSH_READ_BLOCK_SIZE 1024
46 #define SSH_READ_TIMEOUT 10000
47
48 #define WIRESHARK_CAPTURE_POINT "WIRESHARK_CAPTURE_POINT"
49 #define WIRESHARK_CAPTURE_BUFFER "WIRESHARK_CAPTURE_BUFFER"
50 #define WIRESHARK_CAPTURE_ACCESSLIST "WIRESHARK_CAPTURE_ACCESSLIST"
51
52 #define PCAP_SNAPLEN 0xffff
53
54 #define PACKET_MAX_SIZE 65535
55
56 #define MINIMUM_IOS_MAJOR 12
57 #define MINIMUM_IOS_MINOR 4
58
59 /* Status of the parser */
60 enum {
61         CISCODUMP_PARSER_STARTING,
62         CISCODUMP_PARSER_IN_PACKET,
63         CISCODUMP_PARSER_IN_HEADER,
64         CISCODUMP_PARSER_END_PACKET,
65         CISCODUMP_PARSER_ERROR
66 };
67
68 enum {
69         EXTCAP_BASE_OPTIONS_ENUM,
70         OPT_HELP,
71         OPT_VERSION,
72         OPT_REMOTE_HOST,
73         OPT_REMOTE_PORT,
74         OPT_REMOTE_USERNAME,
75         OPT_REMOTE_PASSWORD,
76         OPT_REMOTE_INTERFACE,
77         OPT_REMOTE_FILTER,
78         OPT_SSHKEY,
79         OPT_SSHKEY_PASSPHRASE,
80         OPT_REMOTE_COUNT
81 };
82
83 static struct option longopts[] = {
84         EXTCAP_BASE_OPTIONS,
85         { "help", no_argument, NULL, OPT_HELP},
86         { "version", no_argument, NULL, OPT_VERSION},
87         SSH_BASE_OPTIONS,
88         { 0, 0, 0, 0}
89 };
90
91 static char* interfaces_list_to_filter(GSList* interfaces, unsigned int remote_port)
92 {
93         GString* filter = g_string_new(NULL);
94         GSList* cur;
95
96         if (interfaces) {
97                 g_string_append_printf(filter, "deny tcp host %s any eq %u, deny tcp any eq %u host %s",
98                                 (char*)interfaces->data, remote_port, remote_port, (char*)interfaces->data);
99                 cur = g_slist_next(interfaces);
100                 while (cur) {
101                         g_string_append_printf(filter, ", deny tcp host %s any eq %u, deny tcp any eq %u host %s",
102                                 (char*)cur->data, remote_port, remote_port, (char*)cur->data);
103                         cur = g_slist_next(cur);
104                 }
105                 g_string_append_printf(filter, ", permit ip any any");
106         }
107
108         return g_string_free(filter, FALSE);
109 }
110
111 static char* local_interfaces_to_filter(const unsigned int remote_port)
112 {
113         GSList* interfaces = local_interfaces_to_list();
114         char* filter = interfaces_list_to_filter(interfaces, remote_port);
115         g_slist_free_full(interfaces, g_free);
116         return filter;
117 }
118
119 /* Read bytes from the channel. If bytes == -1, read all data (until timeout). If outbuf != NULL, data are stored there */
120 static int read_output_bytes(ssh_channel channel, int bytes, char* outbuf)
121 {
122         char chr;
123         int total;
124         int bytes_read;
125
126         total = (bytes > 0 ? bytes : G_MAXINT);
127         bytes_read = 0;
128
129         while(ssh_channel_read_timeout(channel, &chr, 1, 0, 2000) > 0 && bytes_read < total) {
130                 g_debug("%c", chr);
131                 if (chr == '^')
132                         return EXIT_FAILURE;
133                 if (outbuf)
134                         outbuf[bytes_read] = chr;
135                 bytes_read++;
136         }
137         return EXIT_SUCCESS;
138 }
139
140 static void ciscodump_cleanup(ssh_session sshs, ssh_channel channel, const char* iface, const char* cfilter)
141 {
142         if (channel) {
143                 if (read_output_bytes(channel, -1, NULL) == EXIT_SUCCESS) {
144                         ssh_channel_printf(channel, "monitor capture point stop %s\n", WIRESHARK_CAPTURE_POINT);
145                         ssh_channel_printf(channel, "no monitor capture point ip cef %s %s\n", WIRESHARK_CAPTURE_POINT, iface);
146                         ssh_channel_printf(channel, "no monitor capture buffer %s\n", WIRESHARK_CAPTURE_BUFFER);
147                         if (cfilter) {
148                                 ssh_channel_printf(channel, "configure terminal\n");
149                                 ssh_channel_printf(channel, "no ip access-list ex %s\n", WIRESHARK_CAPTURE_ACCESSLIST);
150                         }
151                         read_output_bytes(channel, -1, NULL);
152                 }
153         }
154         ssh_cleanup(&sshs, &channel);
155 }
156
157 static int wait_until_data(ssh_channel channel, const guint32 count)
158 {
159         long unsigned got = 0;
160         char output[SSH_READ_BLOCK_SIZE];
161         char* output_ptr;
162         guint rounds = 100;
163
164         while (got < count && rounds--) {
165                 if (ssh_channel_printf(channel, "show monitor capture buffer %s parameters\n", WIRESHARK_CAPTURE_BUFFER) == EXIT_FAILURE) {
166                         g_warning("Can't write to channel");
167                         return EXIT_FAILURE;
168                 }
169                 if (read_output_bytes(channel, SSH_READ_BLOCK_SIZE, output) == EXIT_FAILURE)
170                         return EXIT_FAILURE;
171
172                 output_ptr = g_strstr_len(output, strlen(output), "Packets");
173                 if (!output_ptr) {
174                         g_warning("Error in sscanf()");
175                         return EXIT_FAILURE;
176                 } else {
177                         if (sscanf(output_ptr, "Packets : %lu", &got) != 1)
178                                 return EXIT_FAILURE;
179                 }
180         }
181         g_debug("All packets got: dumping");
182         return EXIT_SUCCESS;
183 }
184
185 static int parse_line(char* packet _U_, unsigned* offset, char* line, int status)
186 {
187         char** parts;
188         char** part;
189         guint32 value;
190         size_t size;
191
192         if (strlen(line) <= 1) {
193                 if (status == CISCODUMP_PARSER_IN_PACKET)
194                         return CISCODUMP_PARSER_END_PACKET;
195                 else
196                         return status;
197         }
198
199         /* we got the packet header                                    */
200         /* The packet header is a line like:                           */
201         /* 16:09:37.171 ITA Mar 18 2016 : IPv4 LES CEF    : Gi0/1 None */
202         if (g_regex_match_simple("^\\d{2}:\\d{2}:\\d{2}.\\d+ .*", line, G_REGEX_CASELESS, G_REGEX_MATCH_ANCHORED)) {
203                 return CISCODUMP_PARSER_IN_HEADER;
204         }
205
206         /* we got a line of the packet                                                          */
207         /* A line looks like                                                                    */
208         /* <address>: <1st group> <2nd group> <3rd group> <4th group> <ascii representation>    */
209         /* ABCDEF01: 01020304 05060708 090A0B0C 0D0E0F10 ................                       */
210         /* Note that any of the 4 groups are optional and that a group can be 1 to 4 bytes long */
211         parts = g_regex_split_simple(
212                 "^[\\dA-F]{8,8}:\\s+([\\dA-F]{2,8})\\s+([\\dA-F]{2,8}){0,1}\\s+([\\dA-F]{2,8}){0,1}\\s+([\\dA-F]{2,8}){0,1}.*",
213                 line, G_REGEX_CASELESS, G_REGEX_MATCH_ANCHORED);
214
215         part = parts;
216         while(*part) {
217                 if (strlen(*part) > 1) {
218                         value = (guint32)strtoul(*part, NULL, 16);
219                         value = ntohl(value);
220                         size = strlen(*part) / 2;
221                         memcpy(packet + *offset, &value, size);
222                         *offset += (guint32)size;
223                 }
224                 part++;
225         }
226         g_strfreev(parts);
227         return CISCODUMP_PARSER_IN_PACKET;
228 }
229
230 static void ssh_loop_read(ssh_channel channel, FILE* fp, const guint32 count)
231 {
232         char line[SSH_READ_BLOCK_SIZE];
233         char chr;
234         unsigned offset = 0;
235         unsigned packet_size = 0;
236         char* packet;
237         time_t curtime = time(NULL);
238         int err;
239         guint64 bytes_written;
240         long unsigned packets = 0;
241         int status = CISCODUMP_PARSER_STARTING;
242
243         /* This is big enough to put on the heap */
244         packet = (char*)g_malloc(PACKET_MAX_SIZE);
245
246         do {
247                 if (ssh_channel_read_timeout(channel, &chr, 1, FALSE, SSH_READ_TIMEOUT) == SSH_ERROR) {
248                         g_warning("Error reading from channel");
249                         g_free(packet);
250                         return;
251                 }
252
253                 if (chr != '\n') {
254                         line[offset] = chr;
255                         offset++;
256                 } else {
257                         /* Parse the current line */
258                         line[offset] = '\0';
259                         status = parse_line(packet, &packet_size, line, status);
260
261                         if (status == CISCODUMP_PARSER_END_PACKET) {
262                                 /* dump the packet to the pcap file */
263                                 libpcap_write_packet(fp, curtime, (guint32)(curtime / 1000), packet_size, packet_size, packet, &bytes_written, &err);
264                                 g_debug("Dumped packet %lu size: %u", packets, packet_size);
265                                 packet_size = 0;
266                                 status = CISCODUMP_PARSER_STARTING;
267                                 packets++;
268                         }
269                         offset = 0;
270                 }
271
272         } while(packets < count);
273
274         g_free(packet);
275 }
276
277 static int check_ios_version(ssh_channel channel)
278 {
279         gchar* cmdline = "show version | include Cisco IOS\n";
280         gchar version[255];
281         guint major = 0;
282         guint minor = 0;
283         gchar* cur;
284
285         memset(version, 0x0, 255);
286
287         if (ssh_channel_write(channel, cmdline, (guint32)strlen(cmdline)) == SSH_ERROR)
288                 return FALSE;
289         if (read_output_bytes(channel, (int)strlen(cmdline), NULL) == EXIT_FAILURE)
290                 return FALSE;
291         if (read_output_bytes(channel, 255, version) == EXIT_FAILURE)
292                 return FALSE;
293
294         cur = g_strstr_len(version, strlen(version), "Version");
295         if (cur) {
296                 cur += strlen("Version ");
297                 if (sscanf(cur, "%u.%u", &major, &minor) != 2)
298                         return FALSE;
299
300                 if ((major > MINIMUM_IOS_MAJOR) || (major == MINIMUM_IOS_MAJOR && minor >= MINIMUM_IOS_MINOR)) {
301                         g_debug("Current IOS Version: %u.%u", major, minor);
302                         if (read_output_bytes(channel, -1, NULL) == EXIT_FAILURE)
303                                 return FALSE;
304                         return TRUE;
305                 }
306         }
307
308         g_warning("Invalid IOS version. Minimum version: 12.4, current: %u.%u", major, minor);
309         return FALSE;
310 }
311
312 static ssh_channel run_capture(ssh_session sshs, const char* iface, const char* cfilter, const guint32 count)
313 {
314         char* cmdline = NULL;
315         ssh_channel channel;
316         int ret = 0;
317
318         channel = ssh_channel_new(sshs);
319         if (!channel)
320                 return NULL;
321
322         if (ssh_channel_open_session(channel) != SSH_OK)
323                 goto error;
324
325         if (ssh_channel_request_pty(channel) != SSH_OK)
326                 goto error;
327
328         if (ssh_channel_change_pty_size(channel, 80, 24) != SSH_OK)
329                 goto error;
330
331         if (ssh_channel_request_shell(channel) != SSH_OK)
332                 goto error;
333
334         if (!check_ios_version(channel))
335                 goto error;
336
337         if (ssh_channel_printf(channel, "terminal length 0\n") == EXIT_FAILURE)
338                 goto error;
339
340         if (ssh_channel_printf(channel, "monitor capture buffer %s max-size 9500\n", WIRESHARK_CAPTURE_BUFFER) == EXIT_FAILURE)
341                 goto error;
342
343         if (ssh_channel_printf(channel, "monitor capture buffer %s limit packet-count %u\n", WIRESHARK_CAPTURE_BUFFER, count) == EXIT_FAILURE)
344                 goto error;
345
346         if (cfilter) {
347                 gchar* multiline_filter;
348                 gchar* chr;
349
350                 if (ssh_channel_printf(channel, "configure terminal\n") == EXIT_FAILURE)
351                         goto error;
352
353                 if (ssh_channel_printf(channel, "ip access-list ex %s\n", WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
354                         goto error;
355
356                 multiline_filter = g_strdup(cfilter);
357                 chr = multiline_filter;
358                 while((chr = g_strstr_len(chr, strlen(chr), ",")) != NULL) {
359                         chr[0] = '\n';
360                         g_debug("Splitting filter into multiline");
361                 }
362                 ret = ssh_channel_write(channel, multiline_filter, (uint32_t)strlen(multiline_filter));
363                 g_free(multiline_filter);
364                 if (ret == SSH_ERROR)
365                         goto error;
366
367                 if (ssh_channel_printf(channel, "\nend\n") == EXIT_FAILURE)
368                         goto error;
369
370                 if (ssh_channel_printf(channel, "monitor capture buffer %s filter access-list %s\n",
371                                 WIRESHARK_CAPTURE_BUFFER, WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
372                         goto error;
373         }
374
375         if (ssh_channel_printf(channel, "monitor capture point ip cef %s %s both\n", WIRESHARK_CAPTURE_POINT,
376                         iface) == EXIT_FAILURE)
377                 goto error;
378
379         if (ssh_channel_printf(channel, "monitor capture point associate %s %s \n", WIRESHARK_CAPTURE_POINT,
380                         WIRESHARK_CAPTURE_BUFFER) == EXIT_FAILURE)
381                 goto error;
382
383         if (ssh_channel_printf(channel, "monitor capture point start %s\n", WIRESHARK_CAPTURE_POINT) == EXIT_FAILURE)
384                 goto error;
385
386         if (read_output_bytes(channel, -1, NULL) == EXIT_FAILURE)
387                 goto error;
388
389         if (wait_until_data(channel, count) == EXIT_FAILURE)
390                 goto error;
391
392         if (read_output_bytes(channel, -1, NULL) == EXIT_FAILURE)
393                 goto error;
394
395         cmdline = g_strdup_printf("show monitor capture buffer %s dump\n", WIRESHARK_CAPTURE_BUFFER);
396         if (ssh_channel_printf(channel, cmdline) == EXIT_FAILURE)
397                 goto error;
398
399         if (read_output_bytes(channel, (int)strlen(cmdline), NULL) == EXIT_FAILURE)
400                 goto error;
401
402         g_free(cmdline);
403         return channel;
404 error:
405         g_free(cmdline);
406         g_warning("Error running ssh remote command");
407         read_output_bytes(channel, -1, NULL);
408
409         ssh_channel_close(channel);
410         ssh_channel_free(channel);
411         return NULL;
412 }
413
414 static int ssh_open_remote_connection(const char* hostname, const unsigned int port, const char* username, const char* password,
415         const char* sshkey, const char* sshkey_passphrase, const char* iface, const char* cfilter,
416         const guint32 count, const char* fifo)
417 {
418         ssh_session sshs;
419         ssh_channel channel;
420         FILE* fp = stdout;
421         guint64 bytes_written = 0;
422         int err;
423         int ret = EXIT_FAILURE;
424         char* err_info = NULL;
425
426         if (g_strcmp0(fifo, "-")) {
427                 /* Open or create the output file */
428                 fp = fopen(fifo, "w");
429                 if (!fp) {
430                         g_warning("Error creating output file: %s", g_strerror(errno));
431                         return EXIT_FAILURE;
432                 }
433         }
434
435         sshs = create_ssh_connection(hostname, port, username, password, sshkey, sshkey_passphrase, &err_info);
436         if (!sshs) {
437                 g_warning("Error creating connection: %s", err_info);
438                 goto cleanup;
439         }
440
441         if (!libpcap_write_file_header(fp, 1, PCAP_SNAPLEN, FALSE, &bytes_written, &err)) {
442                 g_warning("Can't write pcap file header");
443                 goto cleanup;
444         }
445
446         channel = run_capture(sshs, iface, cfilter, count);
447         if (!channel) {
448                 ret = EXIT_FAILURE;
449                 goto cleanup;
450         }
451
452         /* read from channel and write into fp */
453         ssh_loop_read(channel, fp, count);
454
455         /* clean up and exit */
456         ciscodump_cleanup(sshs, channel, iface, cfilter);
457
458         ret = EXIT_SUCCESS;
459 cleanup:
460         if (fp != stdout)
461                 fclose(fp);
462
463         return ret;
464 }
465
466 static int list_config(char *interface, unsigned int remote_port)
467 {
468         unsigned inc = 0;
469         char* ipfilter;
470
471         if (!interface) {
472                 g_warning("No interface specified.");
473                 return EXIT_FAILURE;
474         }
475
476         if (g_strcmp0(interface, CISCODUMP_EXTCAP_INTERFACE)) {
477                 g_warning("interface must be %s", CISCODUMP_EXTCAP_INTERFACE);
478                 return EXIT_FAILURE;
479         }
480
481         ipfilter = local_interfaces_to_filter(remote_port);
482
483         printf("arg {number=%u}{call=--remote-host}{display=Remote SSH server address}"
484                 "{type=string}{tooltip=The remote SSH host. It can be both "
485                 "an IP address or a hostname}{required=true}\n", inc++);
486         printf("arg {number=%u}{call=--remote-port}{display=Remote SSH server port}"
487                 "{type=unsigned}{default=22}{tooltip=The remote SSH host port (1-65535)}"
488                 "{range=1,65535}\n", inc++);
489         printf("arg {number=%u}{call=--remote-username}{display=Remote SSH server username}"
490                 "{type=string}{default=%s}{tooltip=The remote SSH username. If not provided, "
491                 "the current user will be used}\n", inc++, g_get_user_name());
492         printf("arg {number=%u}{call=--remote-password}{display=Remote SSH server password}"
493                 "{type=password}{tooltip=The SSH password, used when other methods (SSH agent "
494                 "or key files) are unavailable.}\n", inc++);
495         printf("arg {number=%u}{call=--sshkey}{display=Path to SSH private key}"
496                 "{type=fileselect}{tooltip=The path on the local filesystem of the private ssh key}\n",
497                 inc++);
498         printf("arg {number=%u}{call--sshkey-passphrase}{display=SSH key passphrase}"
499                 "{type=password}{tooltip=Passphrase to unlock the SSH private key}\n",
500                 inc++);
501         printf("arg {number=%u}{call=--remote-interface}{display=Remote interface}"
502                 "{type=string}{required=true}{tooltip=The remote network interface used for capture"
503                 "}\n", inc++);
504         printf("arg {number=%u}{call=--remote-filter}{display=Remote capture filter}"
505                 "{type=string}{tooltip=The remote capture filter}", inc++);
506         if (ipfilter)
507                 printf("{default=%s}", ipfilter);
508         printf("\n");
509         printf("arg {number=%u}{call=--remote-count}{display=Packets to capture}"
510                 "{type=unsigned}{required=true}{tooltip=The number of remote packets to capture.}\n",
511                 inc++);
512
513         g_free(ipfilter);
514
515         return EXIT_SUCCESS;
516 }
517
518 int main(int argc, char **argv)
519 {
520         int result;
521         int option_idx = 0;
522         int i;
523         char* remote_host = NULL;
524         guint16 remote_port = 22;
525         char* remote_username = NULL;
526         char* remote_password = NULL;
527         char* remote_interface = NULL;
528         char* sshkey = NULL;
529         char* sshkey_passphrase = NULL;
530         char* remote_filter = NULL;
531         guint32 count = 0;
532         int ret = EXIT_FAILURE;
533         extcap_parameters * extcap_conf = g_new0(extcap_parameters, 1);
534         char* help_header = NULL;
535
536 #ifdef _WIN32
537         WSADATA wsaData;
538
539         attach_parent_console();
540 #endif  /* _WIN32 */
541
542         extcap_base_set_util_info(extcap_conf, argv[0], CISCODUMP_VERSION_MAJOR, CISCODUMP_VERSION_MINOR,
543                 CISCODUMP_VERSION_RELEASE, NULL);
544         extcap_base_register_interface(extcap_conf, CISCODUMP_EXTCAP_INTERFACE, "Cisco remote capture", 147, "Remote capture dependent DLT");
545
546         help_header = g_strdup_printf(
547                 " %s --extcap-interfaces\n"
548                 " %s --extcap-interface=%s --extcap-dlts\n"
549                 " %s --extcap-interface=%s --extcap-config\n"
550                 " %s --extcap-interface=%s --remote-host myhost --remote-port 22222 "
551                 "--remote-username myuser --remote-interface gigabit0/0 "
552                 "--fifo=FILENAME --capture\n", argv[0], argv[0], CISCODUMP_EXTCAP_INTERFACE, argv[0],
553                 CISCODUMP_EXTCAP_INTERFACE, argv[0], CISCODUMP_EXTCAP_INTERFACE);
554         extcap_help_add_header(extcap_conf, help_header);
555         g_free(help_header);
556
557         extcap_help_add_option(extcap_conf, "--help", "print this help");
558         extcap_help_add_option(extcap_conf, "--version", "print the version");
559         extcap_help_add_option(extcap_conf, "--verbose", "print more messages");
560         extcap_help_add_option(extcap_conf, "--remote-host <host>", "the remote SSH host");
561         extcap_help_add_option(extcap_conf, "--remote-port <port>", "the remote SSH port (default: 22)");
562         extcap_help_add_option(extcap_conf, "--remote-username <username>", "the remote SSH username (default: the current user)");
563         extcap_help_add_option(extcap_conf, "--remote-password <password>", "the remote SSH password. "
564                 "If not specified, ssh-agent and ssh-key are used");
565         extcap_help_add_option(extcap_conf, "--sshkey <public key path>", "the path of the ssh key");
566         extcap_help_add_option(extcap_conf, "--sshkey-passphrase <public key passphrase>", "the passphrase to unlock public ssh");
567         extcap_help_add_option(extcap_conf, "--remote-interface <iface>", "the remote capture interface");
568         extcap_help_add_option(extcap_conf, "--remote-filter <filter>", "a filter for remote capture "
569                 "(default: don't capture data for lal interfaces IPs)");
570
571         opterr = 0;
572         optind = 0;
573
574         if (argc == 1) {
575                 extcap_help_print(extcap_conf);
576                 goto end;
577         }
578
579         for (i = 0; i < argc; i++)
580                 g_debug("%s ", argv[i]);
581
582         while ((result = getopt_long(argc, argv, ":", longopts, &option_idx)) != -1) {
583
584                 switch (result) {
585
586                 case OPT_HELP:
587                         extcap_help_print(extcap_conf);
588                         ret = EXIT_SUCCESS;
589                         goto end;
590
591                 case OPT_VERSION:
592                         printf("%s\n", extcap_conf->version);
593                         goto end;
594
595                 case OPT_REMOTE_HOST:
596                         g_free(remote_host);
597                         remote_host = g_strdup(optarg);
598                         break;
599
600                 case OPT_REMOTE_PORT:
601                         if (!ws_strtou16(optarg, NULL, &remote_port) || remote_port == 0) {
602                                 g_warning("Invalid port: %s", optarg);
603                                 goto end;
604                         }
605                         break;
606
607                 case OPT_REMOTE_USERNAME:
608                         g_free(remote_username);
609                         remote_username = g_strdup(optarg);
610                         break;
611
612                 case OPT_REMOTE_PASSWORD:
613                         g_free(remote_password);
614                         remote_password = g_strdup(optarg);
615                         memset(optarg, 'X', strlen(optarg));
616                         break;
617
618                 case OPT_SSHKEY:
619                         g_free(sshkey);
620                         sshkey = g_strdup(optarg);
621                         break;
622
623                 case OPT_SSHKEY_PASSPHRASE:
624                         g_free(sshkey_passphrase);
625                         sshkey_passphrase = g_strdup(optarg);
626                         memset(optarg, 'X', strlen(optarg));
627                         break;
628
629                 case OPT_REMOTE_INTERFACE:
630                         g_free(remote_interface);
631                         remote_interface = g_strdup(optarg);
632                         break;
633
634                 case OPT_REMOTE_FILTER:
635                         g_free(remote_filter);
636                         remote_filter = g_strdup(optarg);
637                         break;
638
639                 case OPT_REMOTE_COUNT:
640                         if (!ws_strtou32(optarg, NULL, &count)) {
641                                 g_warning("Invalid packet count: %s", optarg);
642                                 goto end;
643                         }
644                         break;
645
646                 case ':':
647                         /* missing option argument */
648                         g_warning("Option '%s' requires an argument", argv[optind - 1]);
649                         break;
650
651                 default:
652                         if (!extcap_base_parse_options(extcap_conf, result - EXTCAP_OPT_LIST_INTERFACES, optarg)) {
653                                 g_warning("Invalid option: %s", argv[optind - 1]);
654                                 goto end;
655                         }
656                 }
657         }
658
659         if (optind != argc) {
660                 g_warning("Unexpected extra option: %s", argv[optind]);
661                 goto end;
662         }
663
664         if (extcap_base_handle_interface(extcap_conf)) {
665                 ret = EXIT_SUCCESS;
666                 goto end;
667         }
668
669         if (extcap_conf->show_config) {
670                 ret = list_config(extcap_conf->interface, remote_port);
671                 goto end;
672         }
673
674 #ifdef _WIN32
675         result = WSAStartup(MAKEWORD(1,1), &wsaData);
676         if (result != 0) {
677                 g_warning("ERROR: WSAStartup failed with error: %d", result);
678                 goto end;
679         }
680 #endif  /* _WIN32 */
681
682         if (extcap_conf->capture) {
683                 if (!remote_host) {
684                         g_warning("Missing parameter: --remote-host");
685                         goto end;
686                 }
687
688                 if (!remote_interface) {
689                         g_warning("ERROR: No interface specified (--remote-interface)");
690                         goto end;
691                 }
692                 if (count == 0) {
693                         g_warning("ERROR: count of packets must be specified (--remote-count)");
694                         goto end;
695                 }
696
697                 ret = ssh_open_remote_connection(remote_host, remote_port, remote_username,
698                         remote_password, sshkey, sshkey_passphrase, remote_interface,
699                         remote_filter, count, extcap_conf->fifo);
700         } else {
701                 g_debug("You should not come here... maybe some parameter missing?");
702                 ret = EXIT_FAILURE;
703         }
704
705 end:
706         g_free(remote_host);
707         g_free(remote_username);
708         g_free(remote_password);
709         g_free(remote_interface);
710         g_free(sshkey);
711         g_free(sshkey_passphrase);
712         g_free(remote_filter);
713         extcap_base_cleanup(&extcap_conf);
714         return ret;
715 }
716
717 #ifdef _WIN32
718 int _stdcall
719 WinMain (struct HINSTANCE__ *hInstance,
720         struct HINSTANCE__ *hPrevInstance,
721         char               *lpszCmdLine,
722         int                 nCmdShow)
723 {
724         return main(__argc, __argv);
725 }
726 #endif
727
728 /*
729  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
730  *
731  * Local variables:
732  * c-basic-offset: 4
733  * tab-width: 4
734  * indent-tabs-mode: t
735  * End:
736  *
737  * vi: set shiftwidth=4 tabstop=4 noexpandtab:
738  * :indentSize=4:tabSize=4:noTabs=false:
739  */