Update README.md
[oweals/nmrpflash.git] / main.c
1 /**
2  * nmrpflash - Netgear Unbrick Utility
3  * Copyright (C) 2016 Joseph Lehner <joseph.c.lehner@gmail.com>
4  *
5  * nmrpflash is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * nmrpflash is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with nmrpflash.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19
20 #include <unistd.h>
21 #include <getopt.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include "nmrpd.h"
25
26 int verbosity = 0;
27
28 void usage(FILE *fp)
29 {
30         fprintf(fp,
31                         "Usage: nmrpflash [OPTIONS...]\n"
32                         "\n"
33                         "Options (-i, and -f or -c are mandatory):\n"
34                         " -a <ipaddr>     IP address to assign to target device\n"
35                         " -A <ipaddr>     IP address to assign to selected interface\n"
36                         " -B              Blind mode (don't wait for response packets)\n"
37                         " -c <command>    Command to run before (or instead of) TFTP upload\n"
38                         " -f <firmware>   Firmware file\n"
39                         " -F <filename>   Remote filename to use during TFTP upload\n"
40                         " -i <interface>  Network interface directly connected to device\n"
41                         " -m <mac>        MAC address of target device (xx:xx:xx:xx:xx:xx)\n"
42                         " -M <netmask>    Subnet mask to assign to target device\n"
43                         " -t <timeout>    Timeout (in milliseconds) for NMRP packets\n"
44                         " -T <timeout>    Time (seconds) to wait after successfull TFTP upload\n"
45                         " -p <port>       Port to use for TFTP upload\n"
46 #ifdef NMRPFLASH_SET_REGION
47                         " -R <region>     Set device region (NA, WW, GR, PR, RU, BZ, IN, KO, JP)\n"
48 #endif
49 #ifdef NMRPFLASH_TFTP_TEST
50                         " -U              Test TFTP upload\n"
51 #endif
52                         " -v              Be verbose\n"
53                         " -V              Print version and exit\n"
54                         " -L              List network interfaces\n"
55                         " -h              Show this screen\n"
56                         "\n"
57                         "Example: (run as "
58 #ifndef NMRPFLASH_WINDOWS
59                         "root"
60 #else
61                         "administrator"
62 #endif
63                         ")\n\n"
64 #ifndef NMRPFLASH_WINDOWS
65                         "# nmrpflash -i eth0 -f firmware.bin\n"
66 #else
67                         "C:\\> nmrpflash.exe -i net0 -f firmware.bin\n"
68 #endif
69                         "\n"
70                         "When using -c, the environment variables IP, PORT, NETMASK\n"
71                         "and MAC are set to the device IP address, TFTP port, subnet\n"
72                         "mask and MAC address, respectively.\n"
73                         "\n"
74                         "nmrpflash %s, Copyright (C) 2016 Joseph C. Lehner\n"
75                         "nmrpflash is free software, licensed under the GNU GPLv3.\n"
76                         "Source code at https://github.com/jclehner/nmrpflash\n"
77                         "\n",
78                         NMRPFLASH_VERSION
79           );
80 }
81
82 #ifdef NMRPFLASH_WINDOWS
83 void require_admin()
84 {
85         SID_IDENTIFIER_AUTHORITY auth = SECURITY_NT_AUTHORITY;
86         PSID group = NULL;
87         BOOL admin, success = AllocateAndInitializeSid(
88                 &auth, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
89                 0, 0, 0, 0, 0, 0, &group
90         );
91
92         if (success) {
93                 success = CheckTokenMembership(NULL, group, &admin);
94                 FreeSid(group);
95                 if (success) {
96                         if (!admin) {
97                                 fprintf(stderr, "Error: must be run as administrator\n");
98                                 exit(1);
99                         } else {
100                                 return;
101                         }
102                 }
103         }
104
105         fprintf(stderr, "Warning: failed to check administrator privileges\n");
106 }
107
108 void show_exit_prompt()
109 {
110         DWORD pid;
111         HWND win = GetConsoleWindow();
112         if (!win || !GetWindowThreadProcessId(win, &pid)) {
113                 return;
114         }
115
116         if (GetCurrentProcessId() == pid) {
117                 printf("Press any key to exit\n");
118                 getch();
119         }
120 }
121 #else
122 void require_admin()
123 {
124         if (getuid() != 0) {
125                 fprintf(stderr, "Error: must be run as root\n");
126                 exit(1);
127         }
128 }
129 #endif
130
131 int main(int argc, char **argv)
132 {
133         int c, val, max;
134         bool list = false, have_dest_mac = false;
135         struct nmrpd_args args = {
136                 .rx_timeout = 200 * 1000,
137                 .ul_timeout = 5 * 60 * 1000,
138                 .tftpcmd = NULL,
139                 .file_local = NULL,
140                 .file_remote = NULL,
141                 .ipaddr_intf = NULL,
142                 .ipaddr = NULL,
143                 .ipmask = "255.255.255.0",
144                 .intf = NULL,
145                 .mac = "ff:ff:ff:ff:ff:ff",
146                 .op = NMRP_UPLOAD_FW,
147                 .port = 69,
148                 .region = NULL,
149                 .blind = false,
150         };
151 #ifdef NMRPFLASH_WINDOWS
152         char *newpath = NULL;
153         char *oldpath = NULL;
154         char *windir = NULL;
155         WSADATA wsa;
156
157         atexit(&show_exit_prompt);
158
159         val = WSAStartup(MAKEWORD(2, 2), &wsa);
160         if (val != 0) {
161                 win_perror2("WSAStartup", val);
162                 return 1;
163         }
164
165
166 #ifndef _WIN64
167         // This dirty hack works around the WOW64 file system redirector[1], which would prevent
168         // us from calling programs residing in %windir%\System32 when running on a 64bit system
169         // (since nmrpflash is currently shipped as 32bit only).
170         //
171         // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa384187(v=vs.85).aspx
172
173         oldpath = getenv("PATH");
174         windir = getenv("WINDIR");
175         if (oldpath && windir) {
176                 newpath = malloc(strlen(oldpath) + strlen(windir) + 32);
177                 sprintf(newpath, "%s;%s\\Sysnative", oldpath, windir);
178                 SetEnvironmentVariable("PATH", newpath);
179                 free(newpath);
180         }
181 #endif
182 #endif
183
184         opterr = 0;
185
186         while ((c = getopt(argc, argv, "a:A:Bc:f:F:i:m:M:p:R:t:T:hLVvU")) != -1) {
187                 max = 0x7fffffff;
188                 switch (c) {
189                         case 'a':
190                                 args.ipaddr = optarg;
191                                 break;
192                         case 'A':
193                                 args.ipaddr_intf = optarg;
194                                 break;
195                         case 'B':
196                                 args.blind = true;
197                                 break;
198                         case 'c':
199                                 args.tftpcmd = optarg;
200                                 break;
201                         case 'f':
202                                 args.file_local = optarg;
203                                 break;
204                         case 'F':
205                                 args.file_remote = optarg;
206                                 break;
207                         case 'i':
208                                 args.intf = optarg;
209                                 break;
210                         case 'm':
211                                 args.mac = optarg;
212                                 have_dest_mac = true;
213                                 break;
214                         case 'M':
215                                 args.ipmask = optarg;
216                                 break;
217 #ifdef NMRPFLASH_SET_REGION
218                         case 'R':
219                                 args.region = optarg;
220                                 break;
221 #endif
222                         case 'p':
223                         case 'T':
224                         case 't':
225                                 if (c == 'p') {
226                                         max = 0xffff;
227                                 }
228
229                                 val = atoi(optarg);
230                                 if (val <= 0 || val > max) {
231                                         fprintf(stderr, "Invalid numeric value for -%c.\n", c);
232                                         return 1;
233                                 }
234
235                                 if (c == 'p') {
236                                         args.port = val;
237                                 } else if (c == 't') {
238                                         args.rx_timeout = val * 1000;
239                                 } else if (c == 'T') {
240                                         args.ul_timeout = val * 1000;
241                                 }
242
243                                 break;
244                         case 'V':
245                                 printf("nmrpflash %s\n", NMRPFLASH_VERSION);
246                                 val = 0;
247                                 goto out;
248                         case 'v':
249                                 ++verbosity;
250                                 break;
251                         case 'L':
252                                 list = true;
253                                 break;
254                         case 'h':
255                                 usage(stdout);
256                                 val = 0;
257                                 goto out;
258 #ifdef NMRPFLASH_TFTP_TEST
259                         case 'U':
260                                 if (args.ipaddr && args.file_local) {
261                                         val = tftp_put(&args);
262                                         goto out;
263                                 }
264                                 /* fall through */
265 #endif
266                         default:
267                                 usage(stderr);
268                                 val = 1;
269                                 goto out;
270                 }
271         }
272
273         if (args.ipaddr_intf && !args.ipaddr) {
274                 fprintf(stderr, "Error: cannot use -A <ipaddr> without using -a <ipaddr>.\n");
275                 return 1;
276         }
277
278         if (args.blind && !have_dest_mac) {
279                 fprintf(stderr, "Error: use of -B requires -m <mac>.\n");
280                 return 1;
281         }
282
283 #ifndef NMRPFLASH_FUZZ
284         if (!list && ((!args.file_local && !args.tftpcmd) || !args.intf)) {
285                 usage(stderr);
286                 return 1;
287         }
288
289         if (!list) {
290                 require_admin();
291         }
292 #endif
293         val = !list ? nmrp_do(&args) : ethsock_list_all();
294
295 out:
296 #ifdef NMRPFLASH_WINDOWS
297         WSACleanup();
298 #endif
299         return val;
300 }