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