aboutsummaryrefslogtreecommitdiff
path: root/networking
diff options
context:
space:
mode:
Diffstat (limited to 'networking')
-rw-r--r--networking/httpd_ssi.c4
-rw-r--r--networking/libiproute/ipaddress.c3
-rw-r--r--networking/libiproute/iproute.c59
-rw-r--r--networking/libiproute/libnetlink.c3
-rw-r--r--networking/libiproute/utils.c86
-rw-r--r--networking/libiproute/utils.h6
-rw-r--r--networking/ntpd.c2
-rw-r--r--networking/ping.c12
-rw-r--r--networking/tc.c10
-rw-r--r--networking/tftp.c10
-rw-r--r--networking/udhcp/Config.src18
-rw-r--r--networking/udhcp/common.c2
-rw-r--r--networking/udhcp/dhcpc.c44
-rw-r--r--networking/udhcp/leases.c40
-rw-r--r--networking/wget.c630
15 files changed, 540 insertions, 389 deletions
diff --git a/networking/httpd_ssi.c b/networking/httpd_ssi.c
index 87f43fcfa..cfe64eb46 100644
--- a/networking/httpd_ssi.c
+++ b/networking/httpd_ssi.c
@@ -52,9 +52,9 @@ httpd_ssi.c -o httpd_ssi
52 52
53static char* skip_whitespace(char *s) 53static char* skip_whitespace(char *s)
54{ 54{
55 while (*s == ' ' || *s == '\t') ++s; 55 while (*s == ' ' || *s == '\t') ++s;
56 56
57 return s; 57 return s;
58} 58}
59 59
60static char line[64 * 1024]; 60static char line[64 * 1024];
diff --git a/networking/libiproute/ipaddress.c b/networking/libiproute/ipaddress.c
index 397a8ee34..b3748e8c5 100644
--- a/networking/libiproute/ipaddress.c
+++ b/networking/libiproute/ipaddress.c
@@ -162,6 +162,8 @@ static NOINLINE int print_linkinfo(const struct nlmsghdr *n)
162 printf("master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1)); 162 printf("master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
163 } 163 }
164#endif 164#endif
165/* IFLA_OPERSTATE was added to kernel with the same commit as IFF_DORMANT */
166#ifdef IFF_DORMANT
165 if (tb[IFLA_OPERSTATE]) { 167 if (tb[IFLA_OPERSTATE]) {
166 static const char operstate_labels[] ALIGN1 = 168 static const char operstate_labels[] ALIGN1 =
167 "UNKNOWN\0""NOTPRESENT\0""DOWN\0""LOWERLAYERDOWN\0" 169 "UNKNOWN\0""NOTPRESENT\0""DOWN\0""LOWERLAYERDOWN\0"
@@ -169,6 +171,7 @@ static NOINLINE int print_linkinfo(const struct nlmsghdr *n)
169 printf("state %s ", nth_string(operstate_labels, 171 printf("state %s ", nth_string(operstate_labels,
170 *(uint8_t *)RTA_DATA(tb[IFLA_OPERSTATE]))); 172 *(uint8_t *)RTA_DATA(tb[IFLA_OPERSTATE])));
171 } 173 }
174#endif
172 if (G_filter.showqueue) 175 if (G_filter.showqueue)
173 print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME])); 176 print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME]));
174 177
diff --git a/networking/libiproute/iproute.c b/networking/libiproute/iproute.c
index f6071b463..f8a67d9ee 100644
--- a/networking/libiproute/iproute.c
+++ b/networking/libiproute/iproute.c
@@ -31,8 +31,8 @@ struct filter_t {
31 //int type; - read-only 31 //int type; - read-only
32 //int typemask; - unused 32 //int typemask; - unused
33 //int tos, tosmask; - unused 33 //int tos, tosmask; - unused
34 int iif, iifmask; 34 int iif;
35 int oif, oifmask; 35 int oif;
36 //int realm, realmmask; - unused 36 //int realm, realmmask; - unused
37 //inet_prefix rprefsrc; - read-only 37 //inet_prefix rprefsrc; - read-only
38 inet_prefix rvia; 38 inet_prefix rvia;
@@ -82,7 +82,7 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
82{ 82{
83 struct rtmsg *r = NLMSG_DATA(n); 83 struct rtmsg *r = NLMSG_DATA(n);
84 int len = n->nlmsg_len; 84 int len = n->nlmsg_len;
85 struct rtattr * tb[RTA_MAX+1]; 85 struct rtattr *tb[RTA_MAX+1];
86 char abuf[256]; 86 char abuf[256];
87 inet_prefix dst; 87 inet_prefix dst;
88 inet_prefix src; 88 inet_prefix src;
@@ -159,8 +159,21 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
159 } 159 }
160 160
161 memset(tb, 0, sizeof(tb)); 161 memset(tb, 0, sizeof(tb));
162 memset(&src, 0, sizeof(src));
163 memset(&dst, 0, sizeof(dst));
162 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); 164 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
163 165
166 if (tb[RTA_SRC]) {
167 src.bitlen = r->rtm_src_len;
168 src.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
169 memcpy(src.data, RTA_DATA(tb[RTA_SRC]), src.bytelen);
170 }
171 if (tb[RTA_DST]) {
172 dst.bitlen = r->rtm_dst_len;
173 dst.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
174 memcpy(dst.data, RTA_DATA(tb[RTA_DST]), dst.bytelen);
175 }
176
164 if (G_filter.rdst.family 177 if (G_filter.rdst.family
165 && inet_addr_match(&dst, &G_filter.rdst, G_filter.rdst.bitlen) 178 && inet_addr_match(&dst, &G_filter.rdst, G_filter.rdst.bitlen)
166 ) { 179 ) {
@@ -182,23 +195,32 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
182 ) { 195 ) {
183 return 0; 196 return 0;
184 } 197 }
185 if (G_filter.flushb 198 if (G_filter.oif != 0) {
186 && r->rtm_family == AF_INET6 199 if (!tb[RTA_OIF])
187 && r->rtm_dst_len == 0 200 return 0;
188 && r->rtm_type == RTN_UNREACHABLE 201 if (G_filter.oif != *(int*)RTA_DATA(tb[RTA_OIF]))
189 && tb[RTA_PRIORITY] 202 return 0;
190 && *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1
191 ) {
192 return 0;
193 } 203 }
194 204
195 if (G_filter.flushb) { 205 if (G_filter.flushb) {
196 struct nlmsghdr *fn; 206 struct nlmsghdr *fn;
207
208 /* We are creating route flush commands */
209
210 if (r->rtm_family == AF_INET6
211 && r->rtm_dst_len == 0
212 && r->rtm_type == RTN_UNREACHABLE
213 && tb[RTA_PRIORITY]
214 && *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1
215 ) {
216 return 0;
217 }
218
197 if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) { 219 if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) {
198 if (flush_update()) 220 if (flush_update())
199 bb_error_msg_and_die("flush"); 221 bb_error_msg_and_die("flush");
200 } 222 }
201 fn = (struct nlmsghdr*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp)); 223 fn = (void*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp));
202 memcpy(fn, n, n->nlmsg_len); 224 memcpy(fn, n, n->nlmsg_len);
203 fn->nlmsg_type = RTM_DELROUTE; 225 fn->nlmsg_type = RTM_DELROUTE;
204 fn->nlmsg_flags = NLM_F_REQUEST; 226 fn->nlmsg_flags = NLM_F_REQUEST;
@@ -208,6 +230,8 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
208 return 0; 230 return 0;
209 } 231 }
210 232
233 /* We are printing routes */
234
211 if (n->nlmsg_type == RTM_DELROUTE) { 235 if (n->nlmsg_type == RTM_DELROUTE) {
212 printf("Deleted "); 236 printf("Deleted ");
213 } 237 }
@@ -257,10 +281,12 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
257 RTA_DATA(tb[RTA_GATEWAY]), 281 RTA_DATA(tb[RTA_GATEWAY]),
258 abuf, sizeof(abuf))); 282 abuf, sizeof(abuf)));
259 } 283 }
260 if (tb[RTA_OIF] && G_filter.oifmask != -1) { 284 if (tb[RTA_OIF]) {
261 printf("dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF]))); 285 printf("dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
262 } 286 }
263 287
288 /* Todo: parse & show "proto kernel", "scope link" here */
289
264 if (tb[RTA_PREFSRC] && /*G_filter.rprefsrc.bitlen - always 0*/ 0 != host_len) { 290 if (tb[RTA_PREFSRC] && /*G_filter.rprefsrc.bitlen - always 0*/ 0 != host_len) {
265 /* Do not use format_host(). It is our local addr 291 /* Do not use format_host(). It is our local addr
266 and symbolic name will not be useful. 292 and symbolic name will not be useful.
@@ -292,7 +318,7 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
292 printf(" error %d", ci->rta_error); 318 printf(" error %d", ci->rta_error);
293 } 319 }
294 } 320 }
295 if (tb[RTA_IIF] && G_filter.iifmask != -1) { 321 if (tb[RTA_IIF] && G_filter.iif == 0) {
296 printf(" iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF]))); 322 printf(" iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
297 } 323 }
298 bb_putchar('\n'); 324 bb_putchar('\n');
@@ -413,7 +439,8 @@ IF_FEATURE_IP_RULE(ARG_table,)
413 NEXT_ARG(); 439 NEXT_ARG();
414 } 440 }
415 if ((**argv < '0' || **argv > '9') 441 if ((**argv < '0' || **argv > '9')
416 && rtnl_rtntype_a2n(&type, *argv) == 0) { 442 && rtnl_rtntype_a2n(&type, *argv) == 0
443 ) {
417 NEXT_ARG(); 444 NEXT_ARG();
418 req.r.rtm_type = type; 445 req.r.rtm_type = type;
419 ok |= type_ok; 446 ok |= type_ok;
@@ -662,12 +689,10 @@ static int iproute_list_or_flush(char **argv, int flush)
662 if (id) { 689 if (id) {
663 idx = xll_name_to_index(id); 690 idx = xll_name_to_index(id);
664 G_filter.iif = idx; 691 G_filter.iif = idx;
665 G_filter.iifmask = -1;
666 } 692 }
667 if (od) { 693 if (od) {
668 idx = xll_name_to_index(od); 694 idx = xll_name_to_index(od);
669 G_filter.oif = idx; 695 G_filter.oif = idx;
670 G_filter.oifmask = -1;
671 } 696 }
672 } 697 }
673 698
diff --git a/networking/libiproute/libnetlink.c b/networking/libiproute/libnetlink.c
index 7291ee2f1..c7533a4a7 100644
--- a/networking/libiproute/libnetlink.c
+++ b/networking/libiproute/libnetlink.c
@@ -55,6 +55,7 @@ int FAST_FUNC xrtnl_wilddump_request(struct rtnl_handle *rth, int family, int ty
55 return rtnl_send(rth, (void*)&req, sizeof(req)); 55 return rtnl_send(rth, (void*)&req, sizeof(req));
56} 56}
57 57
58//TODO: pass rth->fd instead of full rth?
58int FAST_FUNC rtnl_send(struct rtnl_handle *rth, char *buf, int len) 59int FAST_FUNC rtnl_send(struct rtnl_handle *rth, char *buf, int len)
59{ 60{
60 struct sockaddr_nl nladdr; 61 struct sockaddr_nl nladdr;
@@ -392,7 +393,7 @@ void FAST_FUNC parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, in
392 if (rta->rta_type <= max) { 393 if (rta->rta_type <= max) {
393 tb[rta->rta_type] = rta; 394 tb[rta->rta_type] = rta;
394 } 395 }
395 rta = RTA_NEXT(rta,len); 396 rta = RTA_NEXT(rta, len);
396 } 397 }
397 if (len) { 398 if (len) {
398 bb_error_msg("deficit %d, rta_len=%d!", len, rta->rta_len); 399 bb_error_msg("deficit %d, rta_len=%d!", len, rta->rta_len);
diff --git a/networking/libiproute/utils.c b/networking/libiproute/utils.c
index d32db8de5..d0fe30605 100644
--- a/networking/libiproute/utils.c
+++ b/networking/libiproute/utils.c
@@ -83,20 +83,43 @@ int get_addr_1(inet_prefix *addr, char *name, int family)
83 return 0; 83 return 0;
84 } 84 }
85 85
86 addr->family = AF_INET;
87 if (family != AF_UNSPEC && family != AF_INET) 86 if (family != AF_UNSPEC && family != AF_INET)
88 return -1; 87 return -1;
88
89 /* Try to parse it as IPv4 */
90 addr->family = AF_INET;
91#if 0 /* Doesn't handle e.g. "10.10", for example, "ip r l root 10.10/16" */
89 if (inet_pton(AF_INET, name, addr->data) <= 0) 92 if (inet_pton(AF_INET, name, addr->data) <= 0)
90 return -1; 93 return -1;
94#else
95 {
96 unsigned i = 0;
97 unsigned n = 0;
98 const char *cp = name - 1;
99 while (*++cp) {
100 if ((unsigned char)(*cp - '0') <= 9) {
101 n = 10 * n + (unsigned char)(*cp - '0');
102 if (n >= 256)
103 return -1;
104 ((uint8_t*)addr->data)[i] = n;
105 continue;
106 }
107 if (*cp == '.' && ++i <= 3) {
108 n = 0;
109 continue;
110 }
111 return -1;
112 }
113 }
114#endif
91 addr->bytelen = 4; 115 addr->bytelen = 4;
92 addr->bitlen = -1; 116 addr->bitlen = -1;
117
93 return 0; 118 return 0;
94} 119}
95 120
96static int get_prefix_1(inet_prefix *dst, char *arg, int family) 121static void get_prefix_1(inet_prefix *dst, char *arg, int family)
97{ 122{
98 int err;
99 unsigned plen;
100 char *slash; 123 char *slash;
101 124
102 memset(dst, 0, sizeof(*dst)); 125 memset(dst, 0, sizeof(*dst));
@@ -108,48 +131,50 @@ static int get_prefix_1(inet_prefix *dst, char *arg, int family)
108 dst->family = family; 131 dst->family = family;
109 /*dst->bytelen = 0; - done by memset */ 132 /*dst->bytelen = 0; - done by memset */
110 /*dst->bitlen = 0;*/ 133 /*dst->bitlen = 0;*/
111 return 0; 134 return;
112 } 135 }
113 136
114 slash = strchr(arg, '/'); 137 slash = strchr(arg, '/');
115 if (slash) 138 if (slash)
116 *slash = '\0'; 139 *slash = '\0';
117 err = get_addr_1(dst, arg, family); 140
118 if (err == 0) { 141 if (get_addr_1(dst, arg, family) == 0) {
119 dst->bitlen = (dst->family == AF_INET6) ? 128 : 32; 142 dst->bitlen = (dst->family == AF_INET6) ? 128 : 32;
120 if (slash) { 143 if (slash) {
144 unsigned plen;
121 inet_prefix netmask_pfx; 145 inet_prefix netmask_pfx;
122 146
123 netmask_pfx.family = AF_UNSPEC; 147 netmask_pfx.family = AF_UNSPEC;
124 plen = bb_strtou(slash + 1, NULL, 0); 148 plen = bb_strtou(slash + 1, NULL, 0);
125 if ((errno || plen > dst->bitlen) 149 if ((errno || plen > dst->bitlen)
126 && (get_addr_1(&netmask_pfx, slash + 1, family))) 150 && get_addr_1(&netmask_pfx, slash + 1, family) != 0
127 err = -1; 151 ) {
128 else if (netmask_pfx.family == AF_INET) { 152 goto bad;
153 }
154 if (netmask_pfx.family == AF_INET) {
129 /* fill in prefix length of dotted quad */ 155 /* fill in prefix length of dotted quad */
130 uint32_t mask = ntohl(netmask_pfx.data[0]); 156 uint32_t mask = ntohl(netmask_pfx.data[0]);
131 uint32_t host = ~mask; 157 uint32_t host = ~mask;
132 158
133 /* a valid netmask must be 2^n - 1 */ 159 /* a valid netmask must be 2^n - 1 */
134 if (!(host & (host + 1))) { 160 if (host & (host + 1))
135 for (plen = 0; mask; mask <<= 1) 161 goto bad;
136 ++plen; 162
137 if (plen <= dst->bitlen) { 163 for (plen = 0; mask; mask <<= 1)
138 dst->bitlen = plen; 164 ++plen;
139 /* dst->flags |= PREFIXLEN_SPECIFIED; */ 165 if (plen > dst->bitlen)
140 } else 166 goto bad;
141 err = -1; 167 /* dst->flags |= PREFIXLEN_SPECIFIED; */
142 } else
143 err = -1;
144 } else {
145 /* plain prefix */
146 dst->bitlen = plen;
147 } 168 }
169 dst->bitlen = plen;
148 } 170 }
149 } 171 }
172
150 if (slash) 173 if (slash)
151 *slash = '/'; 174 *slash = '/';
152 return err; 175 return;
176 bad:
177 bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "inet", "prefix", arg);
153} 178}
154 179
155int get_addr(inet_prefix *dst, char *arg, int family) 180int get_addr(inet_prefix *dst, char *arg, int family)
@@ -163,15 +188,12 @@ int get_addr(inet_prefix *dst, char *arg, int family)
163 return 0; 188 return 0;
164} 189}
165 190
166int get_prefix(inet_prefix *dst, char *arg, int family) 191void get_prefix(inet_prefix *dst, char *arg, int family)
167{ 192{
168 if (family == AF_PACKET) { 193 if (family == AF_PACKET) {
169 bb_error_msg_and_die("\"%s\" may be inet %s, but it is not allowed in this context", arg, "prefix"); 194 bb_error_msg_and_die("\"%s\" may be inet %s, but it is not allowed in this context", arg, "prefix");
170 } 195 }
171 if (get_prefix_1(dst, arg, family)) { 196 get_prefix_1(dst, arg, family);
172 bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "inet", "prefix", arg);
173 }
174 return 0;
175} 197}
176 198
177uint32_t get_addr32(char *name) 199uint32_t get_addr32(char *name)
@@ -204,10 +226,10 @@ void duparg2(const char *key, const char *arg)
204 bb_error_msg_and_die("either \"%s\" is duplicate, or \"%s\" is garbage", key, arg); 226 bb_error_msg_and_die("either \"%s\" is duplicate, or \"%s\" is garbage", key, arg);
205} 227}
206 228
207int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits) 229int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits)
208{ 230{
209 uint32_t *a1 = a->data; 231 const uint32_t *a1 = a->data;
210 uint32_t *a2 = b->data; 232 const uint32_t *a2 = b->data;
211 int words = bits >> 5; 233 int words = bits >> 5;
212 234
213 bits &= 0x1f; 235 bits &= 0x1f;
diff --git a/networking/libiproute/utils.h b/networking/libiproute/utils.h
index 93c9d25d6..5fb4a862c 100644
--- a/networking/libiproute/utils.h
+++ b/networking/libiproute/utils.h
@@ -58,9 +58,9 @@ struct ipx_addr {
58 58
59extern uint32_t get_addr32(char *name); 59extern uint32_t get_addr32(char *name);
60extern int get_addr_1(inet_prefix *dst, char *arg, int family); 60extern int get_addr_1(inet_prefix *dst, char *arg, int family);
61/*extern int get_prefix_1(inet_prefix *dst, char *arg, int family);*/ 61/*extern void get_prefix_1(inet_prefix *dst, char *arg, int family);*/
62extern int get_addr(inet_prefix *dst, char *arg, int family); 62extern int get_addr(inet_prefix *dst, char *arg, int family);
63extern int get_prefix(inet_prefix *dst, char *arg, int family); 63extern void get_prefix(inet_prefix *dst, char *arg, int family);
64 64
65extern unsigned get_unsigned(char *arg, const char *errmsg); 65extern unsigned get_unsigned(char *arg, const char *errmsg);
66extern uint32_t get_u32(char *arg, const char *errmsg); 66extern uint32_t get_u32(char *arg, const char *errmsg);
@@ -77,7 +77,7 @@ extern const char *format_host(int af, int len, void *addr, char *buf, int bufle
77void invarg(const char *, const char *) NORETURN; 77void invarg(const char *, const char *) NORETURN;
78void duparg(const char *, const char *) NORETURN; 78void duparg(const char *, const char *) NORETURN;
79void duparg2(const char *, const char *) NORETURN; 79void duparg2(const char *, const char *) NORETURN;
80int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits); 80int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits);
81 81
82const char *dnet_ntop(int af, const void *addr, char *str, size_t len); 82const char *dnet_ntop(int af, const void *addr, char *str, size_t len);
83int dnet_pton(int af, const char *src, void *addr); 83int dnet_pton(int af, const char *src, void *addr);
diff --git a/networking/ntpd.c b/networking/ntpd.c
index 8fe529edb..3ed05ba29 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -882,7 +882,7 @@ fit(peer_t *p, double rd)
882// /* Do we have a loop? */ 882// /* Do we have a loop? */
883// if (p->refid == p->dstaddr || p->refid == s.refid) 883// if (p->refid == p->dstaddr || p->refid == s.refid)
884// return 0; 884// return 0;
885 return 1; 885 return 1;
886} 886}
887static peer_t* 887static peer_t*
888select_and_cluster(void) 888select_and_cluster(void)
diff --git a/networking/ping.c b/networking/ping.c
index 11ce24eb5..c2ff42e2c 100644
--- a/networking/ping.c
+++ b/networking/ping.c
@@ -419,16 +419,18 @@ static void print_stats_and_exit(int junk UNUSED_PARAM)
419 exit(nreceived == 0 || (deadline && nreceived < pingcount)); 419 exit(nreceived == 0 || (deadline && nreceived < pingcount));
420} 420}
421 421
422static void sendping_tail(void (*sp)(int), const void *pkt, int size_pkt) 422static void sendping_tail(void (*sp)(int), int size_pkt)
423{ 423{
424 int sz; 424 int sz;
425 425
426 CLR((uint16_t)ntransmitted % MAX_DUP_CHK); 426 CLR((uint16_t)ntransmitted % MAX_DUP_CHK);
427 ntransmitted++; 427 ntransmitted++;
428 428
429 size_pkt += datalen;
430
429 /* sizeof(pingaddr) can be larger than real sa size, but I think 431 /* sizeof(pingaddr) can be larger than real sa size, but I think
430 * it doesn't matter */ 432 * it doesn't matter */
431 sz = xsendto(pingsock, pkt, size_pkt, &pingaddr.sa, sizeof(pingaddr)); 433 sz = xsendto(pingsock, G.snd_packet, size_pkt, &pingaddr.sa, sizeof(pingaddr));
432 if (sz != size_pkt) 434 if (sz != size_pkt)
433 bb_error_msg_and_die(bb_msg_write_error); 435 bb_error_msg_and_die(bb_msg_write_error);
434 436
@@ -479,12 +481,12 @@ static void sendping4(int junk UNUSED_PARAM)
479 481
480 pkt->icmp_cksum = in_cksum((unsigned short *) pkt, datalen + ICMP_MINLEN); 482 pkt->icmp_cksum = in_cksum((unsigned short *) pkt, datalen + ICMP_MINLEN);
481 483
482 sendping_tail(sendping4, pkt, datalen + ICMP_MINLEN); 484 sendping_tail(sendping4, ICMP_MINLEN);
483} 485}
484#if ENABLE_PING6 486#if ENABLE_PING6
485static void sendping6(int junk UNUSED_PARAM) 487static void sendping6(int junk UNUSED_PARAM)
486{ 488{
487 struct icmp6_hdr *pkt = alloca(datalen + sizeof(struct icmp6_hdr) + 4); 489 struct icmp6_hdr *pkt = G.snd_packet;
488 490
489 //memset(pkt, 0, datalen + sizeof(struct icmp6_hdr) + 4); 491 //memset(pkt, 0, datalen + sizeof(struct icmp6_hdr) + 4);
490 pkt->icmp6_type = ICMP6_ECHO_REQUEST; 492 pkt->icmp6_type = ICMP6_ECHO_REQUEST;
@@ -498,7 +500,7 @@ static void sendping6(int junk UNUSED_PARAM)
498 500
499 //TODO? pkt->icmp_cksum = in_cksum(...); 501 //TODO? pkt->icmp_cksum = in_cksum(...);
500 502
501 sendping_tail(sendping6, pkt, datalen + sizeof(struct icmp6_hdr)); 503 sendping_tail(sendping6, sizeof(struct icmp6_hdr));
502} 504}
503#endif 505#endif
504 506
diff --git a/networking/tc.c b/networking/tc.c
index 2e2473a70..9b3245546 100644
--- a/networking/tc.c
+++ b/networking/tc.c
@@ -43,17 +43,15 @@ struct globals {
43 __u32 filter_proto; 43 __u32 filter_proto;
44} FIX_ALIASING; 44} FIX_ALIASING;
45#define G (*(struct globals*)&bb_common_bufsiz1) 45#define G (*(struct globals*)&bb_common_bufsiz1)
46struct BUG_G_too_big {
47 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
48};
46#define filter_ifindex (G.filter_ifindex) 49#define filter_ifindex (G.filter_ifindex)
47#define filter_qdisc (G.filter_qdisc) 50#define filter_qdisc (G.filter_qdisc)
48#define filter_parent (G.filter_parent) 51#define filter_parent (G.filter_parent)
49#define filter_prio (G.filter_prio) 52#define filter_prio (G.filter_prio)
50#define filter_proto (G.filter_proto) 53#define filter_proto (G.filter_proto)
51 54#define INIT_G() do { } while (0)
52void BUG_tc_globals_too_big(void);
53#define INIT_G() do { \
54 if (sizeof(G) > COMMON_BUFSIZE) \
55 BUG_tc_globals_too_big(); \
56} while (0)
57 55
58/* Allocates a buffer containing the name of a class id. 56/* Allocates a buffer containing the name of a class id.
59 * The caller must free the returned memory. */ 57 * The caller must free the returned memory. */
diff --git a/networking/tftp.c b/networking/tftp.c
index fcd933f6a..35cf0dbd9 100644
--- a/networking/tftp.c
+++ b/networking/tftp.c
@@ -107,19 +107,19 @@ struct BUG_G_too_big {
107#if ENABLE_FEATURE_TFTP_PROGRESS_BAR 107#if ENABLE_FEATURE_TFTP_PROGRESS_BAR
108static void tftp_progress_update(void) 108static void tftp_progress_update(void)
109{ 109{
110 bb_progress_update(&G.pmt, G.file, 0, G.pos, G.size); 110 bb_progress_update(&G.pmt, 0, G.pos, G.size);
111} 111}
112static void tftp_progress_init(void) 112static void tftp_progress_init(void)
113{ 113{
114 bb_progress_init(&G.pmt); 114 bb_progress_init(&G.pmt, G.file);
115 tftp_progress_update(); 115 tftp_progress_update();
116} 116}
117static void tftp_progress_done(void) 117static void tftp_progress_done(void)
118{ 118{
119 if (G.pmt.inited) { 119 if (is_bb_progress_inited(&G.pmt)) {
120 tftp_progress_update(); 120 tftp_progress_update();
121 bb_putchar_stderr('\n'); 121 bb_putchar_stderr('\n');
122 G.pmt.inited = 0; 122 bb_progress_free(&G.pmt);
123 } 123 }
124} 124}
125#else 125#else
@@ -445,7 +445,7 @@ static int tftp_protocol(
445#if ENABLE_FEATURE_TFTP_PROGRESS_BAR 445#if ENABLE_FEATURE_TFTP_PROGRESS_BAR
446 if (ENABLE_TFTP && remote_file) /* tftp */ 446 if (ENABLE_TFTP && remote_file) /* tftp */
447 G.pos = (block_nr - 1) * (uoff_t)blksize; 447 G.pos = (block_nr - 1) * (uoff_t)blksize;
448 if (G.pmt.inited) 448 if (is_bb_progress_inited(&G.pmt))
449 tftp_progress_update(); 449 tftp_progress_update();
450#endif 450#endif
451 /* Was it final ACK? then exit */ 451 /* Was it final ACK? then exit */
diff --git a/networking/udhcp/Config.src b/networking/udhcp/Config.src
index dcd493f13..750a53a32 100644
--- a/networking/udhcp/Config.src
+++ b/networking/udhcp/Config.src
@@ -39,7 +39,21 @@ config FEATURE_UDHCPD_WRITE_LEASES_EARLY
39 If selected, udhcpd will write a new file with leases every 39 If selected, udhcpd will write a new file with leases every
40 time a new lease has been accepted, thus eliminating the need 40 time a new lease has been accepted, thus eliminating the need
41 to send SIGUSR1 for the initial writing or updating. Any timed 41 to send SIGUSR1 for the initial writing or updating. Any timed
42 rewriting remains undisturbed 42 rewriting remains undisturbed.
43
44config FEATURE_UDHCPD_BASE_IP_ON_MAC
45 bool "Select IP address based on client MAC"
46 default n
47 depends on UDHCPD
48 help
49 If selected, udhcpd will base its selection of IP address to offer
50 on the client's hardware address. Otherwise udhcpd uses the next
51 consecutive free address.
52
53 This reduces the frequency of IP address changes for clients
54 which let their lease expire, and makes consecutive DHCPOFFERS
55 for the same client to (almost always) contain the same
56 IP address.
43 57
44config DHCPD_LEASES_FILE 58config DHCPD_LEASES_FILE
45 string "Absolute path to lease file" 59 string "Absolute path to lease file"
@@ -72,7 +86,7 @@ config FEATURE_UDHCPC_ARPING
72 86
73config FEATURE_UDHCP_PORT 87config FEATURE_UDHCP_PORT
74 bool "Enable '-P port' option for udhcpd and udhcpc" 88 bool "Enable '-P port' option for udhcpd and udhcpc"
75 default y 89 default n
76 depends on UDHCPD || UDHCPC 90 depends on UDHCPD || UDHCPC
77 help 91 help
78 At the cost of ~300 bytes, enables -P port option. 92 At the cost of ~300 bytes, enables -P port option.
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c
index 311f79e7e..0a60261ab 100644
--- a/networking/udhcp/common.c
+++ b/networking/udhcp/common.c
@@ -375,7 +375,7 @@ static NOINLINE void attach_option(
375 new->data = xmalloc(length + OPT_DATA); 375 new->data = xmalloc(length + OPT_DATA);
376 new->data[OPT_CODE] = optflag->code; 376 new->data[OPT_CODE] = optflag->code;
377 new->data[OPT_LEN] = length; 377 new->data[OPT_LEN] = length;
378 memcpy(new->data + OPT_DATA, buffer, length); 378 memcpy(new->data + OPT_DATA, (allocated ? allocated : buffer), length);
379 379
380 curr = opt_list; 380 curr = opt_list;
381 while (*curr && (*curr)->data[OPT_CODE] < optflag->code) 381 while (*curr && (*curr)->data[OPT_CODE] < optflag->code)
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index 7e5ab61fd..d97a404fa 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -714,22 +714,25 @@ static int udhcp_raw_socket(int ifindex)
714 * 714 *
715 * TODO: make conditional? 715 * TODO: make conditional?
716 */ 716 */
717#define SERVER_AND_CLIENT_PORTS ((67 << 16) + 68)
718 static const struct sock_filter filter_instr[] = { 717 static const struct sock_filter filter_instr[] = {
719 /* check for udp */ 718 /* load 9th byte (protocol) */
720 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9), 719 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
721 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0), /* L5, L1, is UDP? */ 720 /* jump to L1 if it is IPPROTO_UDP, else to L4 */
722 /* ugly check for arp on ethernet-like and IPv4 */ 721 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 6),
723 BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */ 722 /* L1: load halfword from offset 6 (flags and frag offset) */
724 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4), /* L3, L4 */ 723 BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6),
725 /* skip IP header */ 724 /* jump to L4 if any bits in frag offset field are set, else to L2 */
726 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */ 725 BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x1fff, 4, 0),
727 /* check udp source and destination ports */ 726 /* L2: skip IP header (load index reg with header len) */
728 BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0), 727 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0),
729 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1), /* L3, L4 */ 728 /* load udp destination port from halfword[header_len + 2] */
730 /* returns */ 729 BPF_STMT(BPF_LD|BPF_H|BPF_IND, 2),
731 BPF_STMT(BPF_RET|BPF_K, 0x0fffffff ), /* L3: pass */ 730 /* jump to L3 if udp dport is CLIENT_PORT, else to L4 */
732 BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */ 731 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1),
732 /* L3: accept packet */
733 BPF_STMT(BPF_RET|BPF_K, 0xffffffff),
734 /* L4: discard packet */
735 BPF_STMT(BPF_RET|BPF_K, 0),
733 }; 736 };
734 static const struct sock_fprog filter_prog = { 737 static const struct sock_fprog filter_prog = {
735 .len = sizeof(filter_instr) / sizeof(filter_instr[0]), 738 .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
@@ -742,18 +745,19 @@ static int udhcp_raw_socket(int ifindex)
742 fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); 745 fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
743 log1("Got raw socket fd %d", fd); //log2? 746 log1("Got raw socket fd %d", fd); //log2?
744 747
745 if (SERVER_PORT == 67 && CLIENT_PORT == 68) { 748 sock.sll_family = AF_PACKET;
746 /* Use only if standard ports are in use */ 749 sock.sll_protocol = htons(ETH_P_IP);
750 sock.sll_ifindex = ifindex;
751 xbind(fd, (struct sockaddr *) &sock, sizeof(sock));
752
753 if (CLIENT_PORT == 68) {
754 /* Use only if standard port is in use */
747 /* Ignoring error (kernel may lack support for this) */ 755 /* Ignoring error (kernel may lack support for this) */
748 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, 756 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
749 sizeof(filter_prog)) >= 0) 757 sizeof(filter_prog)) >= 0)
750 log1("Attached filter to raw socket fd %d", fd); // log? 758 log1("Attached filter to raw socket fd %d", fd); // log?
751 } 759 }
752 760
753 sock.sll_family = AF_PACKET;
754 sock.sll_protocol = htons(ETH_P_IP);
755 sock.sll_ifindex = ifindex;
756 xbind(fd, (struct sockaddr *) &sock, sizeof(sock));
757 log1("Created raw socket"); 761 log1("Created raw socket");
758 762
759 return fd; 763 return fd;
diff --git a/networking/udhcp/leases.c b/networking/udhcp/leases.c
index 7aeb37bae..c5b60b108 100644
--- a/networking/udhcp/leases.c
+++ b/networking/udhcp/leases.c
@@ -137,21 +137,42 @@ uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac)
137 uint32_t addr; 137 uint32_t addr;
138 struct dyn_lease *oldest_lease = NULL; 138 struct dyn_lease *oldest_lease = NULL;
139 139
140 addr = server_config.start_ip; /* addr is in host order here */ 140#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
141 for (; addr <= server_config.end_ip; addr++) { 141 uint32_t stop;
142 unsigned i, hash;
143
144 /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
145 * dispersal even with similarly-valued "strings".
146 */
147 hash = 0;
148 for (i = 0; i < 6; i++)
149 hash += safe_mac[i] + (hash << 6) + (hash << 16) - hash;
150
151 /* pick a seed based on hwaddr then iterate until we find a free address. */
152 addr = server_config.start_ip
153 + (hash % (1 + server_config.end_ip - server_config.start_ip));
154 stop = addr;
155#else
156 addr = server_config.start_ip;
157#define stop (server_config.end_ip + 1)
158#endif
159 do {
142 uint32_t nip; 160 uint32_t nip;
143 struct dyn_lease *lease; 161 struct dyn_lease *lease;
144 162
145 /* ie, 192.168.55.0 */ 163 /* ie, 192.168.55.0 */
146 if ((addr & 0xff) == 0) 164 if ((addr & 0xff) == 0)
147 continue; 165 goto next_addr;
148 /* ie, 192.168.55.255 */ 166 /* ie, 192.168.55.255 */
149 if ((addr & 0xff) == 0xff) 167 if ((addr & 0xff) == 0xff)
150 continue; 168 goto next_addr;
151 nip = htonl(addr); 169 nip = htonl(addr);
170 /* skip our own address */
171 if (nip == server_config.server_nip)
172 goto next_addr;
152 /* is this a static lease addr? */ 173 /* is this a static lease addr? */
153 if (is_nip_reserved(server_config.static_leases, nip)) 174 if (is_nip_reserved(server_config.static_leases, nip))
154 continue; 175 goto next_addr;
155 176
156 lease = find_lease_by_nip(nip); 177 lease = find_lease_by_nip(nip);
157 if (!lease) { 178 if (!lease) {
@@ -162,7 +183,14 @@ uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac)
162 if (!oldest_lease || lease->expires < oldest_lease->expires) 183 if (!oldest_lease || lease->expires < oldest_lease->expires)
163 oldest_lease = lease; 184 oldest_lease = lease;
164 } 185 }
165 } 186
187 next_addr:
188 addr++;
189#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
190 if (addr > server_config.end_ip)
191 addr = server_config.start_ip;
192#endif
193 } while (addr != stop);
166 194
167 if (oldest_lease 195 if (oldest_lease
168 && is_expired_lease(oldest_lease) 196 && is_expired_lease(oldest_lease)
diff --git a/networking/wget.c b/networking/wget.c
index 16594c9bf..bbc161be8 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -10,9 +10,12 @@
10 */ 10 */
11#include "libbb.h" 11#include "libbb.h"
12 12
13//#define log_io(...) bb_error_msg(__VA_ARGS__)
14#define log_io(...) ((void)0)
15
16
13struct host_info { 17struct host_info {
14 // May be used if we ever will want to free() all xstrdup()s... 18 char *allocated;
15 /* char *allocated; */
16 const char *path; 19 const char *path;
17 const char *user; 20 const char *user;
18 char *host; 21 char *host;
@@ -30,17 +33,31 @@ struct globals {
30 const char *curfile; /* Name of current file being transferred */ 33 const char *curfile; /* Name of current file being transferred */
31 bb_progress_t pmt; 34 bb_progress_t pmt;
32#endif 35#endif
36 char *dir_prefix;
37#if ENABLE_FEATURE_WGET_LONG_OPTIONS
38 char *post_data;
39 char *extra_headers;
40#endif
41 char *fname_out; /* where to direct output (-O) */
42 const char *proxy_flag; /* Use proxies if env vars are set */
43 const char *user_agent; /* "User-Agent" header field */
33#if ENABLE_FEATURE_WGET_TIMEOUT 44#if ENABLE_FEATURE_WGET_TIMEOUT
34 unsigned timeout_seconds; 45 unsigned timeout_seconds;
35#endif 46#endif
47 int output_fd;
48 int o_flags;
36 smallint chunked; /* chunked transfer encoding */ 49 smallint chunked; /* chunked transfer encoding */
37 smallint got_clen; /* got content-length: from server */ 50 smallint got_clen; /* got content-length: from server */
51 /* Local downloads do benefit from big buffer.
52 * With 512 byte buffer, it was measured to be
53 * an order of magnitude slower than with big one.
54 */
55 uint64_t just_to_align_next_member;
56 char wget_buf[CONFIG_FEATURE_COPYBUF_KB*1024];
38} FIX_ALIASING; 57} FIX_ALIASING;
39#define G (*(struct globals*)&bb_common_bufsiz1) 58#define G (*ptr_to_globals)
40struct BUG_G_too_big {
41 char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
42};
43#define INIT_G() do { \ 59#define INIT_G() do { \
60 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
44 IF_FEATURE_WGET_TIMEOUT(G.timeout_seconds = 900;) \ 61 IF_FEATURE_WGET_TIMEOUT(G.timeout_seconds = 900;) \
45} while (0) 62} while (0)
46 63
@@ -73,12 +90,16 @@ static void progress_meter(int flag)
73 return; 90 return;
74 91
75 if (flag == PROGRESS_START) 92 if (flag == PROGRESS_START)
76 bb_progress_init(&G.pmt); 93 bb_progress_init(&G.pmt, G.curfile);
77 94
78 bb_progress_update(&G.pmt, G.curfile, G.beg_range, G.transferred, 95 bb_progress_update(&G.pmt,
79 G.chunked ? 0 : G.beg_range + G.transferred + G.content_len); 96 G.beg_range,
97 G.transferred,
98 (G.chunked || !G.got_clen) ? 0 : G.beg_range + G.transferred + G.content_len
99 );
80 100
81 if (flag == PROGRESS_END) { 101 if (flag == PROGRESS_END) {
102 bb_progress_free(&G.pmt);
82 bb_putchar_stderr('\n'); 103 bb_putchar_stderr('\n');
83 G.transferred = 0; 104 G.transferred = 0;
84 } 105 }
@@ -124,48 +145,15 @@ static void strip_ipv6_scope_id(char *host)
124 overlapping_strcpy(scope, cp); 145 overlapping_strcpy(scope, cp);
125} 146}
126 147
127/* Read NMEMB bytes into PTR from STREAM. Returns the number of bytes read,
128 * and a short count if an eof or non-interrupt error is encountered. */
129static size_t safe_fread(void *ptr, size_t nmemb, FILE *stream)
130{
131 size_t ret;
132 char *p = (char*)ptr;
133
134 do {
135 clearerr(stream);
136 errno = 0;
137 ret = fread(p, 1, nmemb, stream);
138 p += ret;
139 nmemb -= ret;
140 } while (nmemb && ferror(stream) && errno == EINTR);
141
142 return p - (char*)ptr;
143}
144
145/* Read a line or SIZE-1 bytes into S, whichever is less, from STREAM.
146 * Returns S, or NULL if an eof or non-interrupt error is encountered. */
147static char *safe_fgets(char *s, int size, FILE *stream)
148{
149 char *ret;
150
151 do {
152 clearerr(stream);
153 errno = 0;
154 ret = fgets(s, size, stream);
155 } while (ret == NULL && ferror(stream) && errno == EINTR);
156
157 return ret;
158}
159
160#if ENABLE_FEATURE_WGET_AUTHENTICATION 148#if ENABLE_FEATURE_WGET_AUTHENTICATION
161/* Base64-encode character string. buf is assumed to be char buf[512]. */ 149/* Base64-encode character string. */
162static char *base64enc_512(char buf[512], const char *str) 150static char *base64enc(const char *str)
163{ 151{
164 unsigned len = strlen(str); 152 unsigned len = strlen(str);
165 if (len > 512/4*3 - 10) /* paranoia */ 153 if (len > sizeof(G.wget_buf)/4*3 - 10) /* paranoia */
166 len = 512/4*3 - 10; 154 len = sizeof(G.wget_buf)/4*3 - 10;
167 bb_uuencode(buf, str, len, bb_uuenc_tbl_base64); 155 bb_uuencode(G.wget_buf, str, len, bb_uuenc_tbl_base64);
168 return buf; 156 return G.wget_buf;
169} 157}
170#endif 158#endif
171 159
@@ -186,43 +174,58 @@ static FILE *open_socket(len_and_sockaddr *lsa)
186 /* hopefully it understands what ESPIPE means... */ 174 /* hopefully it understands what ESPIPE means... */
187 fp = fdopen(xconnect_stream(lsa), "r+"); 175 fp = fdopen(xconnect_stream(lsa), "r+");
188 if (fp == NULL) 176 if (fp == NULL)
189 bb_perror_msg_and_die("fdopen"); 177 bb_perror_msg_and_die(bb_msg_memory_exhausted);
190 178
191 return fp; 179 return fp;
192} 180}
193 181
194static int ftpcmd(const char *s1, const char *s2, FILE *fp, char *buf) 182/* Returns '\n' if it was seen, else '\0'. Trims at first '\r' or '\n' */
183static char fgets_and_trim(FILE *fp)
184{
185 char c;
186 char *buf_ptr;
187
188 if (fgets(G.wget_buf, sizeof(G.wget_buf) - 1, fp) == NULL)
189 bb_perror_msg_and_die("error getting response");
190
191 buf_ptr = strchrnul(G.wget_buf, '\n');
192 c = *buf_ptr;
193 *buf_ptr = '\0';
194 buf_ptr = strchrnul(G.wget_buf, '\r');
195 *buf_ptr = '\0';
196
197 log_io("< %s", G.wget_buf);
198
199 return c;
200}
201
202static int ftpcmd(const char *s1, const char *s2, FILE *fp)
195{ 203{
196 int result; 204 int result;
197 if (s1) { 205 if (s1) {
198 if (!s2) s2 = ""; 206 if (!s2)
207 s2 = "";
199 fprintf(fp, "%s%s\r\n", s1, s2); 208 fprintf(fp, "%s%s\r\n", s1, s2);
200 fflush(fp); 209 fflush(fp);
210 log_io("> %s%s", s1, s2);
201 } 211 }
202 212
203 do { 213 do {
204 char *buf_ptr; 214 fgets_and_trim(fp);
205 215 } while (!isdigit(G.wget_buf[0]) || G.wget_buf[3] != ' ');
206 if (fgets(buf, 510, fp) == NULL) {
207 bb_perror_msg_and_die("error getting response");
208 }
209 buf_ptr = strstr(buf, "\r\n");
210 if (buf_ptr) {
211 *buf_ptr = '\0';
212 }
213 } while (!isdigit(buf[0]) || buf[3] != ' ');
214 216
215 buf[3] = '\0'; 217 G.wget_buf[3] = '\0';
216 result = xatoi_positive(buf); 218 result = xatoi_positive(G.wget_buf);
217 buf[3] = ' '; 219 G.wget_buf[3] = ' ';
218 return result; 220 return result;
219} 221}
220 222
221static void parse_url(char *src_url, struct host_info *h) 223static void parse_url(const char *src_url, struct host_info *h)
222{ 224{
223 char *url, *p, *sp; 225 char *url, *p, *sp;
224 226
225 /* h->allocated = */ url = xstrdup(src_url); 227 free(h->allocated);
228 h->allocated = url = xstrdup(src_url);
226 229
227 if (strncmp(url, "http://", 7) == 0) { 230 if (strncmp(url, "http://", 7) == 0) {
228 h->port = bb_lookup_port("http", "tcp", 80); 231 h->port = bb_lookup_port("http", "tcp", 80);
@@ -278,7 +281,7 @@ static void parse_url(char *src_url, struct host_info *h)
278 sp = h->host; 281 sp = h->host;
279} 282}
280 283
281static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/) 284static char *gethdr(FILE *fp)
282{ 285{
283 char *s, *hdrval; 286 char *s, *hdrval;
284 int c; 287 int c;
@@ -286,43 +289,32 @@ static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
286 /* *istrunc = 0; */ 289 /* *istrunc = 0; */
287 290
288 /* retrieve header line */ 291 /* retrieve header line */
289 if (fgets(buf, bufsiz, fp) == NULL) 292 c = fgets_and_trim(fp);
290 return NULL;
291 293
292 /* see if we are at the end of the headers */ 294 /* end of the headers? */
293 for (s = buf; *s == '\r'; ++s) 295 if (G.wget_buf[0] == '\0')
294 continue;
295 if (*s == '\n')
296 return NULL; 296 return NULL;
297 297
298 /* convert the header name to lower case */ 298 /* convert the header name to lower case */
299 for (s = buf; isalnum(*s) || *s == '-' || *s == '.'; ++s) { 299 for (s = G.wget_buf; isalnum(*s) || *s == '-' || *s == '.'; ++s) {
300 /* tolower for "A-Z", no-op for "0-9a-z-." */ 300 /* tolower for "A-Z", no-op for "0-9a-z-." */
301 *s = (*s | 0x20); 301 *s |= 0x20;
302 } 302 }
303 303
304 /* verify we are at the end of the header name */ 304 /* verify we are at the end of the header name */
305 if (*s != ':') 305 if (*s != ':')
306 bb_error_msg_and_die("bad header line: %s", sanitize_string(buf)); 306 bb_error_msg_and_die("bad header line: %s", sanitize_string(G.wget_buf));
307 307
308 /* locate the start of the header value */ 308 /* locate the start of the header value */
309 *s++ = '\0'; 309 *s++ = '\0';
310 hdrval = skip_whitespace(s); 310 hdrval = skip_whitespace(s);
311 311
312 /* locate the end of header */ 312 if (c != '\n') {
313 while (*s && *s != '\r' && *s != '\n') 313 /* Rats! The buffer isn't big enough to hold the entire header value */
314 ++s; 314 while (c = getc(fp), c != EOF && c != '\n')
315 315 continue;
316 /* end of header found */
317 if (*s) {
318 *s = '\0';
319 return hdrval;
320 } 316 }
321 317
322 /* Rats! The buffer isn't big enough to hold the entire header value */
323 while (c = getc(fp), c != EOF && c != '\n')
324 continue;
325 /* *istrunc = 1; */
326 return hdrval; 318 return hdrval;
327} 319}
328 320
@@ -366,7 +358,6 @@ static char *URL_escape(const char *str)
366 358
367static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa) 359static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa)
368{ 360{
369 char buf[512];
370 FILE *sfp; 361 FILE *sfp;
371 char *str; 362 char *str;
372 int port; 363 int port;
@@ -375,8 +366,8 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
375 target->user = xstrdup("anonymous:busybox@"); 366 target->user = xstrdup("anonymous:busybox@");
376 367
377 sfp = open_socket(lsa); 368 sfp = open_socket(lsa);
378 if (ftpcmd(NULL, NULL, sfp, buf) != 220) 369 if (ftpcmd(NULL, NULL, sfp) != 220)
379 bb_error_msg_and_die("%s", sanitize_string(buf+4)); 370 bb_error_msg_and_die("%s", sanitize_string(G.wget_buf + 4));
380 371
381 /* 372 /*
382 * Splitting username:password pair, 373 * Splitting username:password pair,
@@ -385,24 +376,24 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
385 str = strchr(target->user, ':'); 376 str = strchr(target->user, ':');
386 if (str) 377 if (str)
387 *str++ = '\0'; 378 *str++ = '\0';
388 switch (ftpcmd("USER ", target->user, sfp, buf)) { 379 switch (ftpcmd("USER ", target->user, sfp)) {
389 case 230: 380 case 230:
390 break; 381 break;
391 case 331: 382 case 331:
392 if (ftpcmd("PASS ", str, sfp, buf) == 230) 383 if (ftpcmd("PASS ", str, sfp) == 230)
393 break; 384 break;
394 /* fall through (failed login) */ 385 /* fall through (failed login) */
395 default: 386 default:
396 bb_error_msg_and_die("ftp login: %s", sanitize_string(buf+4)); 387 bb_error_msg_and_die("ftp login: %s", sanitize_string(G.wget_buf + 4));
397 } 388 }
398 389
399 ftpcmd("TYPE I", NULL, sfp, buf); 390 ftpcmd("TYPE I", NULL, sfp);
400 391
401 /* 392 /*
402 * Querying file size 393 * Querying file size
403 */ 394 */
404 if (ftpcmd("SIZE ", target->path, sfp, buf) == 213) { 395 if (ftpcmd("SIZE ", target->path, sfp) == 213) {
405 G.content_len = BB_STRTOOFF(buf+4, NULL, 10); 396 G.content_len = BB_STRTOOFF(G.wget_buf + 4, NULL, 10);
406 if (G.content_len < 0 || errno) { 397 if (G.content_len < 0 || errno) {
407 bb_error_msg_and_die("SIZE value is garbage"); 398 bb_error_msg_and_die("SIZE value is garbage");
408 } 399 }
@@ -412,20 +403,20 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
412 /* 403 /*
413 * Entering passive mode 404 * Entering passive mode
414 */ 405 */
415 if (ftpcmd("PASV", NULL, sfp, buf) != 227) { 406 if (ftpcmd("PASV", NULL, sfp) != 227) {
416 pasv_error: 407 pasv_error:
417 bb_error_msg_and_die("bad response to %s: %s", "PASV", sanitize_string(buf)); 408 bb_error_msg_and_die("bad response to %s: %s", "PASV", sanitize_string(G.wget_buf));
418 } 409 }
419 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage] 410 // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
420 // Server's IP is N1.N2.N3.N4 (we ignore it) 411 // Server's IP is N1.N2.N3.N4 (we ignore it)
421 // Server's port for data connection is P1*256+P2 412 // Server's port for data connection is P1*256+P2
422 str = strrchr(buf, ')'); 413 str = strrchr(G.wget_buf, ')');
423 if (str) str[0] = '\0'; 414 if (str) str[0] = '\0';
424 str = strrchr(buf, ','); 415 str = strrchr(G.wget_buf, ',');
425 if (!str) goto pasv_error; 416 if (!str) goto pasv_error;
426 port = xatou_range(str+1, 0, 255); 417 port = xatou_range(str+1, 0, 255);
427 *str = '\0'; 418 *str = '\0';
428 str = strrchr(buf, ','); 419 str = strrchr(G.wget_buf, ',');
429 if (!str) goto pasv_error; 420 if (!str) goto pasv_error;
430 port += xatou_range(str+1, 0, 255) * 256; 421 port += xatou_range(str+1, 0, 255) * 256;
431 set_nport(lsa, htons(port)); 422 set_nport(lsa, htons(port));
@@ -433,20 +424,19 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
433 *dfpp = open_socket(lsa); 424 *dfpp = open_socket(lsa);
434 425
435 if (G.beg_range) { 426 if (G.beg_range) {
436 sprintf(buf, "REST %"OFF_FMT"u", G.beg_range); 427 sprintf(G.wget_buf, "REST %"OFF_FMT"u", G.beg_range);
437 if (ftpcmd(buf, NULL, sfp, buf) == 350) 428 if (ftpcmd(G.wget_buf, NULL, sfp) == 350)
438 G.content_len -= G.beg_range; 429 G.content_len -= G.beg_range;
439 } 430 }
440 431
441 if (ftpcmd("RETR ", target->path, sfp, buf) > 150) 432 if (ftpcmd("RETR ", target->path, sfp) > 150)
442 bb_error_msg_and_die("bad response to %s: %s", "RETR", sanitize_string(buf)); 433 bb_error_msg_and_die("bad response to %s: %s", "RETR", sanitize_string(G.wget_buf));
443 434
444 return sfp; 435 return sfp;
445} 436}
446 437
447static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd) 438static void NOINLINE retrieve_file_data(FILE *dfp)
448{ 439{
449 char buf[512];
450#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT 440#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
451# if ENABLE_FEATURE_WGET_TIMEOUT 441# if ENABLE_FEATURE_WGET_TIMEOUT
452 unsigned second_cnt; 442 unsigned second_cnt;
@@ -455,7 +445,6 @@ static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd)
455 445
456 polldata.fd = fileno(dfp); 446 polldata.fd = fileno(dfp);
457 polldata.events = POLLIN | POLLPRI; 447 polldata.events = POLLIN | POLLPRI;
458 ndelay_on(polldata.fd);
459#endif 448#endif
460 progress_meter(PROGRESS_START); 449 progress_meter(PROGRESS_START);
461 450
@@ -464,18 +453,30 @@ static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd)
464 453
465 /* Loops only if chunked */ 454 /* Loops only if chunked */
466 while (1) { 455 while (1) {
456
457#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
458 /* Must use nonblocking I/O, otherwise fread will loop
459 * and *block* until it reads full buffer,
460 * which messes up progress bar and/or timeout logic.
461 * Because of nonblocking I/O, we need to dance
462 * very carefully around EAGAIN. See explanation at
463 * clearerr() call.
464 */
465 ndelay_on(polldata.fd);
466#endif
467 while (1) { 467 while (1) {
468 int n; 468 int n;
469 unsigned rdsz; 469 unsigned rdsz;
470 470
471 rdsz = sizeof(buf); 471 rdsz = sizeof(G.wget_buf);
472 if (G.got_clen) { 472 if (G.got_clen) {
473 if (G.content_len < (off_t)sizeof(buf)) { 473 if (G.content_len < (off_t)sizeof(G.wget_buf)) {
474 if ((int)G.content_len <= 0) 474 if ((int)G.content_len <= 0)
475 break; 475 break;
476 rdsz = (unsigned)G.content_len; 476 rdsz = (unsigned)G.content_len;
477 } 477 }
478 } 478 }
479
479#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT 480#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
480# if ENABLE_FEATURE_WGET_TIMEOUT 481# if ENABLE_FEATURE_WGET_TIMEOUT
481 second_cnt = G.timeout_seconds; 482 second_cnt = G.timeout_seconds;
@@ -486,150 +487,107 @@ static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd)
486# if ENABLE_FEATURE_WGET_TIMEOUT 487# if ENABLE_FEATURE_WGET_TIMEOUT
487 if (second_cnt != 0 && --second_cnt == 0) { 488 if (second_cnt != 0 && --second_cnt == 0) {
488 progress_meter(PROGRESS_END); 489 progress_meter(PROGRESS_END);
489 bb_perror_msg_and_die("download timed out"); 490 bb_error_msg_and_die("download timed out");
490 } 491 }
491# endif 492# endif
492 /* Needed for "stalled" indicator */ 493 /* Needed for "stalled" indicator */
493 progress_meter(PROGRESS_BUMP); 494 progress_meter(PROGRESS_BUMP);
494 } 495 }
496
497 /* fread internally uses read loop, which in our case
498 * is usually exited when we get EAGAIN.
499 * In this case, libc sets error marker on the stream.
500 * Need to clear it before next fread to avoid possible
501 * rare false positive ferror below. Rare because usually
502 * fread gets more than zero bytes, and we don't fall
503 * into if (n <= 0) ...
504 */
505 clearerr(dfp);
506 errno = 0;
495#endif 507#endif
496 n = safe_fread(buf, rdsz, dfp); 508 n = fread(G.wget_buf, 1, rdsz, dfp);
509 /* man fread:
510 * If error occurs, or EOF is reached, the return value
511 * is a short item count (or zero).
512 * fread does not distinguish between EOF and error.
513 */
497 if (n <= 0) { 514 if (n <= 0) {
498 if (ferror(dfp)) { 515#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
499 /* perror will not work: ferror doesn't set errno */ 516 if (errno == EAGAIN) /* poll lied, there is no data? */
500 bb_error_msg_and_die(bb_msg_read_error); 517 continue; /* yes */
501 } 518#endif
502 break; 519 if (ferror(dfp))
520 bb_perror_msg_and_die(bb_msg_read_error);
521 break; /* EOF, not error */
503 } 522 }
504 xwrite(output_fd, buf, n); 523
524 xwrite(G.output_fd, G.wget_buf, n);
525
505#if ENABLE_FEATURE_WGET_STATUSBAR 526#if ENABLE_FEATURE_WGET_STATUSBAR
506 G.transferred += n; 527 G.transferred += n;
507 progress_meter(PROGRESS_BUMP); 528 progress_meter(PROGRESS_BUMP);
508#endif 529#endif
509 if (G.got_clen) 530 if (G.got_clen) {
510 G.content_len -= n; 531 G.content_len -= n;
532 if (G.content_len == 0)
533 break;
534 }
511 } 535 }
512 536#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
537 clearerr(dfp);
538 ndelay_off(polldata.fd); /* else fgets can get very unhappy */
539#endif
513 if (!G.chunked) 540 if (!G.chunked)
514 break; 541 break;
515 542
516 safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */ 543 fgets_and_trim(dfp); /* Eat empty line */
517 get_clen: 544 get_clen:
518 safe_fgets(buf, sizeof(buf), dfp); 545 fgets_and_trim(dfp);
519 G.content_len = STRTOOFF(buf, NULL, 16); 546 G.content_len = STRTOOFF(G.wget_buf, NULL, 16);
520 /* FIXME: error check? */ 547 /* FIXME: error check? */
521 if (G.content_len == 0) 548 if (G.content_len == 0)
522 break; /* all done! */ 549 break; /* all done! */
523 G.got_clen = 1; 550 G.got_clen = 1;
524 } 551 }
525 552
553 /* Draw full bar and free its resources */
554 G.chunked = 0; /* makes it show 100% even for chunked download */
555 G.got_clen = 1; /* makes it show 100% even for download of (formerly) unknown size */
526 progress_meter(PROGRESS_END); 556 progress_meter(PROGRESS_END);
527} 557}
528 558
529int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 559static void download_one_url(const char *url)
530int wget_main(int argc UNUSED_PARAM, char **argv)
531{ 560{
532 char buf[512]; 561 bool use_proxy; /* Use proxies if env vars are set */
533 struct host_info server, target;
534 len_and_sockaddr *lsa;
535 unsigned opt;
536 int redir_limit; 562 int redir_limit;
537 char *proxy = NULL; 563 len_and_sockaddr *lsa;
538 char *dir_prefix = NULL;
539#if ENABLE_FEATURE_WGET_LONG_OPTIONS
540 char *post_data;
541 char *extra_headers = NULL;
542 llist_t *headers_llist = NULL;
543#endif
544 FILE *sfp; /* socket to web/ftp server */ 564 FILE *sfp; /* socket to web/ftp server */
545 FILE *dfp; /* socket to ftp server (data) */ 565 FILE *dfp; /* socket to ftp server (data) */
546 char *fname_out; /* where to direct output (-O) */ 566 char *proxy = NULL;
547 int output_fd = -1; 567 char *fname_out_alloc;
548 bool use_proxy; /* Use proxies if env vars are set */ 568 struct host_info server;
549 const char *proxy_flag = "on"; /* Use proxies if env vars are set */ 569 struct host_info target;
550 const char *user_agent = "Wget";/* "User-Agent" header field */
551
552 static const char keywords[] ALIGN1 =
553 "content-length\0""transfer-encoding\0""chunked\0""location\0";
554 enum {
555 KEY_content_length = 1, KEY_transfer_encoding, KEY_chunked, KEY_location
556 };
557#if ENABLE_FEATURE_WGET_LONG_OPTIONS
558 static const char wget_longopts[] ALIGN1 =
559 /* name, has_arg, val */
560 "continue\0" No_argument "c"
561 "spider\0" No_argument "s"
562 "quiet\0" No_argument "q"
563 "output-document\0" Required_argument "O"
564 "directory-prefix\0" Required_argument "P"
565 "proxy\0" Required_argument "Y"
566 "user-agent\0" Required_argument "U"
567#if ENABLE_FEATURE_WGET_TIMEOUT
568 "timeout\0" Required_argument "T"
569#endif
570 /* Ignored: */
571 // "tries\0" Required_argument "t"
572 /* Ignored (we always use PASV): */
573 "passive-ftp\0" No_argument "\xff"
574 "header\0" Required_argument "\xfe"
575 "post-data\0" Required_argument "\xfd"
576 /* Ignored (we don't do ssl) */
577 "no-check-certificate\0" No_argument "\xfc"
578 ;
579#endif
580
581 INIT_G();
582 IF_WIN32_NET(init_winsock();)
583
584#if ENABLE_FEATURE_WGET_LONG_OPTIONS
585 applet_long_options = wget_longopts;
586#endif
587 /* server.allocated = target.allocated = NULL; */
588 opt_complementary = "-1" IF_FEATURE_WGET_TIMEOUT(":T+") IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
589 opt = getopt32(argv, "csqO:P:Y:U:T:" /*ignored:*/ "t:",
590 &fname_out, &dir_prefix,
591 &proxy_flag, &user_agent,
592 IF_FEATURE_WGET_TIMEOUT(&G.timeout_seconds) IF_NOT_FEATURE_WGET_TIMEOUT(NULL),
593 NULL /* -t RETRIES */
594 IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
595 IF_FEATURE_WGET_LONG_OPTIONS(, &post_data)
596 );
597#if ENABLE_FEATURE_WGET_LONG_OPTIONS
598 if (headers_llist) {
599 int size = 1;
600 char *cp;
601 llist_t *ll = headers_llist;
602 while (ll) {
603 size += strlen(ll->data) + 2;
604 ll = ll->link;
605 }
606 extra_headers = cp = xmalloc(size);
607 while (headers_llist) {
608 cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
609 }
610 }
611#endif
612
613 /* TODO: compat issue: should handle "wget URL1 URL2..." */
614 570
571 server.allocated = NULL;
572 target.allocated = NULL;
573 server.user = NULL;
615 target.user = NULL; 574 target.user = NULL;
616 parse_url(argv[optind], &target); 575
576 parse_url(url, &target);
617 577
618 /* Use the proxy if necessary */ 578 /* Use the proxy if necessary */
619 use_proxy = (strcmp(proxy_flag, "off") != 0); 579 use_proxy = (strcmp(G.proxy_flag, "off") != 0);
620 if (use_proxy) { 580 if (use_proxy) {
621 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy"); 581 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
622 if (proxy && proxy[0]) { 582 use_proxy = (proxy && proxy[0]);
623 server.user = NULL; 583 if (use_proxy)
624 parse_url(proxy, &server); 584 parse_url(proxy, &server);
625 } else {
626 use_proxy = 0;
627 }
628 } 585 }
629 if (!use_proxy) { 586 if (!use_proxy) {
630 server.port = target.port; 587 server.port = target.port;
631 if (ENABLE_FEATURE_IPV6) { 588 if (ENABLE_FEATURE_IPV6) {
632 server.host = xstrdup(target.host); 589 //free(server.allocated); - can't be non-NULL
590 server.host = server.allocated = xstrdup(target.host);
633 } else { 591 } else {
634 server.host = target.host; 592 server.host = target.host;
635 } 593 }
@@ -638,50 +596,44 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
638 if (ENABLE_FEATURE_IPV6) 596 if (ENABLE_FEATURE_IPV6)
639 strip_ipv6_scope_id(target.host); 597 strip_ipv6_scope_id(target.host);
640 598
641 /* Guess an output filename, if there was no -O FILE */ 599 /* If there was no -O FILE, guess output filename */
642 if (!(opt & WGET_OPT_OUTNAME)) { 600 fname_out_alloc = NULL;
643 fname_out = bb_get_last_path_component_nostrip(target.path); 601 if (!(option_mask32 & WGET_OPT_OUTNAME)) {
602 G.fname_out = bb_get_last_path_component_nostrip(target.path);
644 /* handle "wget http://kernel.org//" */ 603 /* handle "wget http://kernel.org//" */
645 if (fname_out[0] == '/' || !fname_out[0]) 604 if (G.fname_out[0] == '/' || !G.fname_out[0])
646 fname_out = (char*)"index.html"; 605 G.fname_out = (char*)"index.html";
647 /* -P DIR is considered only if there was no -O FILE */ 606 /* -P DIR is considered only if there was no -O FILE */
648 if (dir_prefix) 607 if (G.dir_prefix)
649 fname_out = concat_path_file(dir_prefix, fname_out); 608 G.fname_out = fname_out_alloc = concat_path_file(G.dir_prefix, G.fname_out);
650 } else {
651 if (LONE_DASH(fname_out)) {
652 /* -O - */
653 output_fd = 1;
654 opt &= ~WGET_OPT_CONTINUE;
655 }
656 } 609 }
657#if ENABLE_FEATURE_WGET_STATUSBAR 610#if ENABLE_FEATURE_WGET_STATUSBAR
658 G.curfile = bb_get_last_path_component_nostrip(fname_out); 611 G.curfile = bb_get_last_path_component_nostrip(G.fname_out);
659#endif 612#endif
660 613
661 /* Impossible?
662 if ((opt & WGET_OPT_CONTINUE) && !fname_out)
663 bb_error_msg_and_die("can't specify continue (-c) without a filename (-O)");
664 */
665
666 /* Determine where to start transfer */ 614 /* Determine where to start transfer */
667 if (opt & WGET_OPT_CONTINUE) { 615 G.beg_range = 0;
668 output_fd = open(fname_out, O_WRONLY); 616 if (option_mask32 & WGET_OPT_CONTINUE) {
669 if (output_fd >= 0) { 617 G.output_fd = open(G.fname_out, O_WRONLY);
670 G.beg_range = xlseek(output_fd, 0, SEEK_END); 618 if (G.output_fd >= 0) {
619 G.beg_range = xlseek(G.output_fd, 0, SEEK_END);
671 } 620 }
672 /* File doesn't exist. We do not create file here yet. 621 /* File doesn't exist. We do not create file here yet.
673 * We are not sure it exists on remove side */ 622 * We are not sure it exists on remote side */
674 } 623 }
675 624
676 redir_limit = 5; 625 redir_limit = 5;
677 resolve_lsa: 626 resolve_lsa:
678 lsa = xhost2sockaddr(server.host, server.port); 627 lsa = xhost2sockaddr(server.host, server.port);
679 if (!(opt & WGET_OPT_QUIET)) { 628 if (!(option_mask32 & WGET_OPT_QUIET)) {
680 char *s = xmalloc_sockaddr2dotted(&lsa->u.sa); 629 char *s = xmalloc_sockaddr2dotted(&lsa->u.sa);
681 fprintf(stderr, "Connecting to %s (%s)\n", server.host, s); 630 fprintf(stderr, "Connecting to %s (%s)\n", server.host, s);
682 free(s); 631 free(s);
683 } 632 }
684 establish_session: 633 establish_session:
634 /*G.content_len = 0; - redundant, got_clen = 0 is enough */
635 G.got_clen = 0;
636 G.chunked = 0;
685 if (use_proxy || !target.is_ftp) { 637 if (use_proxy || !target.is_ftp) {
686 /* 638 /*
687 * HTTP session 639 * HTTP session
@@ -689,6 +641,7 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
689 char *str; 641 char *str;
690 int status; 642 int status;
691 643
644
692 /* Open socket to http server */ 645 /* Open socket to http server */
693 sfp = open_socket(lsa); 646 sfp = open_socket(lsa);
694 647
@@ -698,44 +651,52 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
698 target.is_ftp ? "f" : "ht", target.host, 651 target.is_ftp ? "f" : "ht", target.host,
699 target.path); 652 target.path);
700 } else { 653 } else {
701 if (opt & WGET_OPT_POST_DATA) 654 if (option_mask32 & WGET_OPT_POST_DATA)
702 fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path); 655 fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path);
703 else 656 else
704 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path); 657 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
705 } 658 }
706 659
707 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n", 660 fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
708 target.host, user_agent); 661 target.host, G.user_agent);
662
663 /* Ask server to close the connection as soon as we are done
664 * (IOW: we do not intend to send more requests)
665 */
666 fprintf(sfp, "Connection: close\r\n");
709 667
710#if ENABLE_FEATURE_WGET_AUTHENTICATION 668#if ENABLE_FEATURE_WGET_AUTHENTICATION
711 if (target.user) { 669 if (target.user) {
712 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6, 670 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
713 base64enc_512(buf, target.user)); 671 base64enc(target.user));
714 } 672 }
715 if (use_proxy && server.user) { 673 if (use_proxy && server.user) {
716 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n", 674 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
717 base64enc_512(buf, server.user)); 675 base64enc(server.user));
718 } 676 }
719#endif 677#endif
720 678
721 if (G.beg_range) 679 if (G.beg_range)
722 fprintf(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range); 680 fprintf(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range);
681
723#if ENABLE_FEATURE_WGET_LONG_OPTIONS 682#if ENABLE_FEATURE_WGET_LONG_OPTIONS
724 if (extra_headers) 683 if (G.extra_headers)
725 fputs(extra_headers, sfp); 684 fputs(G.extra_headers, sfp);
726 685
727 if (opt & WGET_OPT_POST_DATA) { 686 if (option_mask32 & WGET_OPT_POST_DATA) {
728 char *estr = URL_escape(post_data); 687 char *estr = URL_escape(G.post_data);
729 fprintf(sfp, "Content-Type: application/x-www-form-urlencoded\r\n"); 688 fprintf(sfp,
730 fprintf(sfp, "Content-Length: %u\r\n" "\r\n" "%s", 689 "Content-Type: application/x-www-form-urlencoded\r\n"
731 (int) strlen(estr), estr); 690 "Content-Length: %u\r\n"
732 /*fprintf(sfp, "Connection: Keep-Alive\r\n\r\n");*/ 691 "\r\n"
733 /*fprintf(sfp, "%s\r\n", estr);*/ 692 "%s",
693 (int) strlen(estr), estr
694 );
734 free(estr); 695 free(estr);
735 } else 696 } else
736#endif 697#endif
737 { /* If "Connection:" is needed, document why */ 698 {
738 fprintf(sfp, /* "Connection: close\r\n" */ "\r\n"); 699 fprintf(sfp, "\r\n");
739 } 700 }
740 701
741 fflush(sfp); 702 fflush(sfp);
@@ -744,10 +705,9 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
744 * Retrieve HTTP response line and check for "200" status code. 705 * Retrieve HTTP response line and check for "200" status code.
745 */ 706 */
746 read_response: 707 read_response:
747 if (fgets(buf, sizeof(buf), sfp) == NULL) 708 fgets_and_trim(sfp);
748 bb_error_msg_and_die("no response from server");
749 709
750 str = buf; 710 str = G.wget_buf;
751 str = skip_non_whitespace(str); 711 str = skip_non_whitespace(str);
752 str = skip_whitespace(str); 712 str = skip_whitespace(str);
753 // FIXME: no error check 713 // FIXME: no error check
@@ -756,7 +716,7 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
756 switch (status) { 716 switch (status) {
757 case 0: 717 case 0:
758 case 100: 718 case 100:
759 while (gethdr(buf, sizeof(buf), sfp /*, &n*/) != NULL) 719 while (gethdr(sfp) != NULL)
760 /* eat all remaining headers */; 720 /* eat all remaining headers */;
761 goto read_response; 721 goto read_response;
762 case 200: 722 case 200:
@@ -796,22 +756,29 @@ However, in real world it was observed that some web servers
796 break; 756 break;
797 /* fall through */ 757 /* fall through */
798 default: 758 default:
799 bb_error_msg_and_die("server returned error: %s", sanitize_string(buf)); 759 bb_error_msg_and_die("server returned error: %s", sanitize_string(G.wget_buf));
800 } 760 }
801 761
802 /* 762 /*
803 * Retrieve HTTP headers. 763 * Retrieve HTTP headers.
804 */ 764 */
805 while ((str = gethdr(buf, sizeof(buf), sfp /*, &n*/)) != NULL) { 765 while ((str = gethdr(sfp)) != NULL) {
806 /* gethdr converted "FOO:" string to lowercase */ 766 static const char keywords[] ALIGN1 =
767 "content-length\0""transfer-encoding\0""location\0";
768 enum {
769 KEY_content_length = 1, KEY_transfer_encoding, KEY_location
770 };
807 smalluint key; 771 smalluint key;
772
773 /* gethdr converted "FOO:" string to lowercase */
774
808 /* strip trailing whitespace */ 775 /* strip trailing whitespace */
809 char *s = strchrnul(str, '\0') - 1; 776 char *s = strchrnul(str, '\0') - 1;
810 while (s >= str && (*s == ' ' || *s == '\t')) { 777 while (s >= str && (*s == ' ' || *s == '\t')) {
811 *s = '\0'; 778 *s = '\0';
812 s--; 779 s--;
813 } 780 }
814 key = index_in_strings(keywords, buf) + 1; 781 key = index_in_strings(keywords, G.wget_buf) + 1;
815 if (key == KEY_content_length) { 782 if (key == KEY_content_length) {
816 G.content_len = BB_STRTOOFF(str, NULL, 10); 783 G.content_len = BB_STRTOOFF(str, NULL, 10);
817 if (G.content_len < 0 || errno) { 784 if (G.content_len < 0 || errno) {
@@ -821,23 +788,23 @@ However, in real world it was observed that some web servers
821 continue; 788 continue;
822 } 789 }
823 if (key == KEY_transfer_encoding) { 790 if (key == KEY_transfer_encoding) {
824 if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked) 791 if (strcmp(str_tolower(str), "chunked") != 0)
825 bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str)); 792 bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str));
826 G.chunked = G.got_clen = 1; 793 G.chunked = 1;
827 } 794 }
828 if (key == KEY_location && status >= 300) { 795 if (key == KEY_location && status >= 300) {
829 if (--redir_limit == 0) 796 if (--redir_limit == 0)
830 bb_error_msg_and_die("too many redirections"); 797 bb_error_msg_and_die("too many redirections");
831 fclose(sfp); 798 fclose(sfp);
832 G.got_clen = 0; 799 if (str[0] == '/') {
833 G.chunked = 0; 800 free(target.allocated);
834 if (str[0] == '/') 801 target.path = target.allocated = xstrdup(str+1);
835 /* free(target.allocated); */
836 target.path = /* target.allocated = */ xstrdup(str+1);
837 /* lsa stays the same: it's on the same server */ 802 /* lsa stays the same: it's on the same server */
838 else { 803 } else {
839 parse_url(str, &target); 804 parse_url(str, &target);
840 if (!use_proxy) { 805 if (!use_proxy) {
806 free(server.allocated);
807 server.allocated = NULL;
841 server.host = target.host; 808 server.host = target.host;
842 /* strip_ipv6_scope_id(target.host); - no! */ 809 /* strip_ipv6_scope_id(target.host); - no! */
843 /* we assume remote never gives us IPv6 addr with scope id */ 810 /* we assume remote never gives us IPv6 addr with scope id */
@@ -862,30 +829,117 @@ However, in real world it was observed that some web servers
862 sfp = prepare_ftp_session(&dfp, &target, lsa); 829 sfp = prepare_ftp_session(&dfp, &target, lsa);
863 } 830 }
864 831
865 if (opt & WGET_OPT_SPIDER) { 832 free(lsa);
866 if (ENABLE_FEATURE_CLEAN_UP)
867 fclose(sfp);
868 return EXIT_SUCCESS;
869 }
870 833
871 if (output_fd < 0) { 834 if (!(option_mask32 & WGET_OPT_SPIDER)) {
872 int o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL; 835 if (G.output_fd < 0)
873 /* compat with wget: -O FILE can overwrite */ 836 G.output_fd = xopen(G.fname_out, G.o_flags);
874 if (opt & WGET_OPT_OUTNAME) 837 retrieve_file_data(dfp);
875 o_flags = O_WRONLY | O_CREAT | O_TRUNC; 838 if (!(option_mask32 & WGET_OPT_OUTNAME)) {
876 output_fd = xopen(fname_out, o_flags); 839 xclose(G.output_fd);
840 G.output_fd = -1;
841 }
877 } 842 }
878 843
879 retrieve_file_data(dfp, output_fd);
880 xclose(output_fd);
881
882 if (dfp != sfp) { 844 if (dfp != sfp) {
883 /* It's ftp. Close it properly */ 845 /* It's ftp. Close data connection properly */
884 fclose(dfp); 846 fclose(dfp);
885 if (ftpcmd(NULL, NULL, sfp, buf) != 226) 847 if (ftpcmd(NULL, NULL, sfp) != 226)
886 bb_error_msg_and_die("ftp error: %s", sanitize_string(buf+4)); 848 bb_error_msg_and_die("ftp error: %s", sanitize_string(G.wget_buf + 4));
887 /* ftpcmd("QUIT", NULL, sfp, buf); - why bother? */ 849 /* ftpcmd("QUIT", NULL, sfp); - why bother? */
850 }
851 fclose(sfp);
852
853 free(server.allocated);
854 free(target.allocated);
855 free(fname_out_alloc);
856}
857
858int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
859int wget_main(int argc UNUSED_PARAM, char **argv)
860{
861#if ENABLE_FEATURE_WGET_LONG_OPTIONS
862 static const char wget_longopts[] ALIGN1 =
863 /* name, has_arg, val */
864 "continue\0" No_argument "c"
865//FIXME: -s isn't --spider, it's --save-headers!
866 "spider\0" No_argument "s"
867 "quiet\0" No_argument "q"
868 "output-document\0" Required_argument "O"
869 "directory-prefix\0" Required_argument "P"
870 "proxy\0" Required_argument "Y"
871 "user-agent\0" Required_argument "U"
872#if ENABLE_FEATURE_WGET_TIMEOUT
873 "timeout\0" Required_argument "T"
874#endif
875 /* Ignored: */
876 // "tries\0" Required_argument "t"
877 /* Ignored (we always use PASV): */
878 "passive-ftp\0" No_argument "\xff"
879 "header\0" Required_argument "\xfe"
880 "post-data\0" Required_argument "\xfd"
881 /* Ignored (we don't do ssl) */
882 "no-check-certificate\0" No_argument "\xfc"
883 ;
884#endif
885
886#if ENABLE_FEATURE_WGET_LONG_OPTIONS
887 llist_t *headers_llist = NULL;
888#endif
889
890 INIT_G();
891 IF_WIN32_NET(init_winsock();)
892
893 IF_FEATURE_WGET_TIMEOUT(G.timeout_seconds = 900;)
894 G.proxy_flag = "on"; /* use proxies if env vars are set */
895 G.user_agent = "Wget"; /* "User-Agent" header field */
896
897#if ENABLE_FEATURE_WGET_LONG_OPTIONS
898 applet_long_options = wget_longopts;
899#endif
900 opt_complementary = "-1" IF_FEATURE_WGET_TIMEOUT(":T+") IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
901 getopt32(argv, "csqO:P:Y:U:T:" /*ignored:*/ "t:",
902 &G.fname_out, &G.dir_prefix,
903 &G.proxy_flag, &G.user_agent,
904 IF_FEATURE_WGET_TIMEOUT(&G.timeout_seconds) IF_NOT_FEATURE_WGET_TIMEOUT(NULL),
905 NULL /* -t RETRIES */
906 IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
907 IF_FEATURE_WGET_LONG_OPTIONS(, &G.post_data)
908 );
909 argv += optind;
910
911#if ENABLE_FEATURE_WGET_LONG_OPTIONS
912 if (headers_llist) {
913 int size = 1;
914 char *cp;
915 llist_t *ll = headers_llist;
916 while (ll) {
917 size += strlen(ll->data) + 2;
918 ll = ll->link;
919 }
920 G.extra_headers = cp = xmalloc(size);
921 while (headers_llist) {
922 cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
923 }
888 } 924 }
925#endif
926
927 G.output_fd = -1;
928 G.o_flags = O_WRONLY | O_CREAT | O_TRUNC | O_EXCL;
929 if (G.fname_out) { /* -O FILE ? */
930 if (LONE_DASH(G.fname_out)) { /* -O - ? */
931 G.output_fd = 1;
932 option_mask32 &= ~WGET_OPT_CONTINUE;
933 }
934 /* compat with wget: -O FILE can overwrite */
935 G.o_flags = O_WRONLY | O_CREAT | O_TRUNC;
936 }
937
938 while (*argv)
939 download_one_url(*argv++);
940
941 if (G.output_fd >= 0)
942 xclose(G.output_fd);
889 943
890 return EXIT_SUCCESS; 944 return EXIT_SUCCESS;
891} 945}