Implement TFTP blksize option
[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 #define NMRPFLASH_SET_REGION
27
28 int verbosity = 0;
29
30 void usage(FILE *fp)
31 {
32         fprintf(fp,
33                         "Usage: nmrpflash [OPTIONS...]\n"
34                         "\n"
35                         "Options (-i, -f and/or -c are mandatory):\n"
36                         " -a <ipaddr>     IP address to assign to target device\n"
37                         " -A <ipaddr>     IP address to assign to seleted interface\n"
38                         " -c <command>    Command to run before (or instead of) TFTP upload\n"
39                         " -f <firmware>   Firmware file\n"
40                         " -F <filename>   Remote filename to use during TFTP upload\n"
41                         " -i <interface>  Network interface directly connected to device\n"
42                         " -m <mac>        MAC address of target device (xx:xx:xx:xx:xx:xx)\n"
43                         " -M <netmask>    Subnet mask to assign to target device\n"
44                         " -t <timeout>    Timeout (in milliseconds) for regular messages\n"
45                         " -T <timeout>    Time (seconds) to wait after successfull TFTP upload\n"
46                         " -p <port>       Port to use for TFTP upload\n"
47 #ifdef NMRPFLASH_SET_REGION
48                         " -R <region>     Set device region (NA, WW, GR, PR, RU, BZ, IN, KO, JP)\n"
49 #endif
50 #ifdef NMRPFLASH_TFTP_TEST
51                         " -U              Test TFTP upload\n"
52 #endif
53                         " -v              Be verbose\n"
54                         " -V              Print version and exit\n"
55                         " -L              List network interfaces\n"
56                         " -h              Show this screen\n"
57                         "\n"
58                         "Example: (run as "
59 #ifndef NMRPFLASH_WINDOWS
60                         "root"
61 #else
62                         "administrator"
63 #endif
64                         ")\n\n"
65 #ifndef NMRPFLASH_WINDOWS
66                         "# nmrpflash -i eth0 -f firmware.bin\n"
67 #else
68                         "C:\\> nmrpflash.exe -i net0 -f firmware.bin\n"
69 #endif
70                         "\n"
71                         "When using -c, the environment variables IP, PORT, NETMASK\n"
72                         "and MAC are set to the device IP address, TFTP port, subnet\n"
73                         "mask and MAC address, respectively.\n"
74                         "\n"
75                         "nmrpflash %s, Copyright (C) 2016 Joseph C. Lehner\n"
76                         "nmrpflash is free software, licensed under the GNU GPLv3.\n"
77                         "Source code at https://github.com/jclehner/nmrpflash\n"
78                         "\n",
79                         NMRPFLASH_VERSION
80           );
81 }
82
83 #ifdef NMRPFLASH_WINDOWS
84 void require_admin()
85 {
86         SID_IDENTIFIER_AUTHORITY auth = SECURITY_NT_AUTHORITY;
87         PSID adminGroup = NULL;
88         BOOL success = AllocateAndInitializeSid(
89                 &auth, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
90                 0, 0, 0, 0, 0, 0, &adminGroup
91         );
92
93         if (success) {
94                 if (CheckTokenMembership(NULL, adminGroup, &success)) {
95                         if (!success) {
96                                 fprintf(stderr, "Error: must be run as administrator\n");
97                                 exit(1);
98                         } else {
99                                 return;
100                         }
101                 }
102                 FreeSid(adminGroup);
103         }
104
105         fprintf(stderr, "Warning: failed to check administrator privileges\n");
106 }
107 #else
108 void require_admin()
109 {
110         if (getuid() != 0) {
111                 fprintf(stderr, "Error: must be run as root\n");
112                 exit(1);
113         }
114 }
115 #endif
116
117 int main(int argc, char **argv)
118 {
119         int c, val, max;
120         int list = 0;
121         struct nmrpd_args args = {
122                 .rx_timeout = 200,
123                 .ul_timeout = 5 * 60 * 1000,
124                 .tftpcmd = NULL,
125                 .file_local = NULL,
126                 .file_remote = NULL,
127                 .ipaddr_intf = NULL,
128                 .ipaddr = NULL,
129                 .ipmask = "255.255.255.0",
130                 .intf = NULL,
131                 .mac = "ff:ff:ff:ff:ff:ff",
132                 .op = NMRP_UPLOAD_FW,
133                 .port = 69,
134                 .region = NULL,
135         };
136 #ifdef NMRPFLASH_WINDOWS
137         char *newpath = NULL;
138         char *oldpath = NULL;
139         char *windir = NULL;
140         WSADATA wsa;
141
142         val = WSAStartup(MAKEWORD(2, 2), &wsa);
143         if (val != 0) {
144                 win_perror2("WSAStartup", val);
145                 return 1;
146         }
147
148
149 #ifndef _WIN64
150         // This dirty hack works around the WOW64 file system redirector[1], which would prevent
151         // us from calling programs residing in %windir%\System32 when running on a 64bit system
152         // (since nmrpflash is currently shipped as 32bit only).
153         //
154         // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/aa384187(v=vs.85).aspx
155
156         oldpath = getenv("PATH");
157         windir = getenv("WINDIR");
158         if (oldpath && windir) {
159                 newpath = malloc(strlen(oldpath) + strlen(windir) + 32);
160                 sprintf(newpath, "%s;%s\\Sysnative", oldpath, windir);
161                 SetEnvironmentVariable("PATH", newpath);
162                 free(newpath);
163         }
164 #endif
165 #endif
166
167         opterr = 0;
168
169         while ((c = getopt(argc, argv, "a:A:c:f:F:i:m:M:p:R:t:T:hLVvU")) != -1) {
170                 max = 0x7fffffff;
171                 switch (c) {
172                         case 'a':
173                                 args.ipaddr = optarg;
174                                 break;
175                         case 'A':
176                                 args.ipaddr_intf = optarg;
177                                 break;
178                         case 'c':
179                                 args.tftpcmd = optarg;
180                                 break;
181                         case 'f':
182                                 args.file_local = optarg;
183                                 break;
184                         case 'F':
185                                 args.file_remote = optarg;
186                                 break;
187                         case 'i':
188                                 args.intf = optarg;
189                                 break;
190                         case 'm':
191                                 args.mac = optarg;
192                                 break;
193                         case 'M':
194                                 args.ipmask = optarg;
195                                 break;
196 #ifdef NMRPFLASH_SET_REGION
197                         case 'R':
198                                 args.region = optarg;
199                                 break;
200 #endif
201                         case 'p':
202                         case 'T':
203                         case 't':
204                                 if (c == 'p') {
205                                         max = 0xffff;
206                                 }
207
208                                 val = atoi(optarg);
209                                 if (val <= 0 || val > max) {
210                                         fprintf(stderr, "Invalid numeric value for -%c.\n", c);
211                                         return 1;
212                                 }
213
214                                 if (c == 'p') {
215                                         args.port = val;
216                                 } else if (c == 't') {
217                                         args.rx_timeout = val;
218                                 } else if (c == 'T') {
219                                         args.ul_timeout = val * 1000;
220                                 }
221
222                                 break;
223                         case 'V':
224                                 printf("nmrpflash %s\n", NMRPFLASH_VERSION);
225                                 val = 0;
226                                 goto out;
227                         case 'v':
228                                 ++verbosity;
229                                 break;
230                         case 'L':
231                                 list = 1;
232                                 break;
233                                 goto out;
234                         case 'h':
235                                 usage(stdout);
236                                 val = 0;
237                                 goto out;
238 #ifdef NMRPFLASH_TFTP_TEST
239                         case 'U':
240                                 if (args.ipaddr && args.file_local) {
241                                         val = tftp_put(&args);
242                                         goto out;
243                                 }
244                                 /* fall through */
245 #endif
246                         default:
247                                 usage(stderr);
248                                 val = 1;
249                                 goto out;
250                 }
251         }
252
253         if (args.ipaddr_intf && !args.ipaddr) {
254                 fprintf(stderr, "Error: cannot use -A <ipaddr> without using -a <ipaddr>.\n");
255                 return 1;
256         }
257
258 #ifndef NMRPFLASH_FUZZ
259         if (!list && ((!args.file_local && !args.tftpcmd) || !args.intf /*|| !args.ipaddr*/)) {
260                 usage(stderr);
261                 return 1;
262         }
263
264         require_admin();
265 #endif
266         val = !list ? nmrp_do(&args) : ethsock_list_all();
267
268 out:
269 #ifdef NMRPFLASH_WINDOWS
270         WSACleanup();
271 #endif
272         return val;
273 }