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