diff options
Diffstat (limited to 'networking')
-rw-r--r-- | networking/httpd_ssi.c | 4 | ||||
-rw-r--r-- | networking/libiproute/ipaddress.c | 3 | ||||
-rw-r--r-- | networking/libiproute/iproute.c | 59 | ||||
-rw-r--r-- | networking/libiproute/libnetlink.c | 3 | ||||
-rw-r--r-- | networking/libiproute/utils.c | 86 | ||||
-rw-r--r-- | networking/libiproute/utils.h | 6 | ||||
-rw-r--r-- | networking/ntpd.c | 2 | ||||
-rw-r--r-- | networking/ping.c | 12 | ||||
-rw-r--r-- | networking/tc.c | 10 | ||||
-rw-r--r-- | networking/tftp.c | 10 | ||||
-rw-r--r-- | networking/udhcp/Config.src | 18 | ||||
-rw-r--r-- | networking/udhcp/common.c | 2 | ||||
-rw-r--r-- | networking/udhcp/dhcpc.c | 44 | ||||
-rw-r--r-- | networking/udhcp/leases.c | 40 | ||||
-rw-r--r-- | networking/wget.c | 630 |
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 | ||
53 | static char* skip_whitespace(char *s) | 53 | static 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 | ||
60 | static char line[64 * 1024]; | 60 | static 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? | ||
58 | int FAST_FUNC rtnl_send(struct rtnl_handle *rth, char *buf, int len) | 59 | int 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 | ||
96 | static int get_prefix_1(inet_prefix *dst, char *arg, int family) | 121 | static 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 | ||
155 | int get_addr(inet_prefix *dst, char *arg, int family) | 180 | int 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 | ||
166 | int get_prefix(inet_prefix *dst, char *arg, int family) | 191 | void 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 | ||
177 | uint32_t get_addr32(char *name) | 199 | uint32_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 | ||
207 | int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits) | 229 | int 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 | ||
59 | extern uint32_t get_addr32(char *name); | 59 | extern uint32_t get_addr32(char *name); |
60 | extern int get_addr_1(inet_prefix *dst, char *arg, int family); | 60 | extern 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);*/ |
62 | extern int get_addr(inet_prefix *dst, char *arg, int family); | 62 | extern int get_addr(inet_prefix *dst, char *arg, int family); |
63 | extern int get_prefix(inet_prefix *dst, char *arg, int family); | 63 | extern void get_prefix(inet_prefix *dst, char *arg, int family); |
64 | 64 | ||
65 | extern unsigned get_unsigned(char *arg, const char *errmsg); | 65 | extern unsigned get_unsigned(char *arg, const char *errmsg); |
66 | extern uint32_t get_u32(char *arg, const char *errmsg); | 66 | extern 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 | |||
77 | void invarg(const char *, const char *) NORETURN; | 77 | void invarg(const char *, const char *) NORETURN; |
78 | void duparg(const char *, const char *) NORETURN; | 78 | void duparg(const char *, const char *) NORETURN; |
79 | void duparg2(const char *, const char *) NORETURN; | 79 | void duparg2(const char *, const char *) NORETURN; |
80 | int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits); | 80 | int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits); |
81 | 81 | ||
82 | const char *dnet_ntop(int af, const void *addr, char *str, size_t len); | 82 | const char *dnet_ntop(int af, const void *addr, char *str, size_t len); |
83 | int dnet_pton(int af, const char *src, void *addr); | 83 | int 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 | } |
887 | static peer_t* | 887 | static peer_t* |
888 | select_and_cluster(void) | 888 | select_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 | ||
422 | static void sendping_tail(void (*sp)(int), const void *pkt, int size_pkt) | 422 | static 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 |
485 | static void sendping6(int junk UNUSED_PARAM) | 487 | static 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) |
46 | struct 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) | |
52 | void 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 |
108 | static void tftp_progress_update(void) | 108 | static 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 | } |
112 | static void tftp_progress_init(void) | 112 | static 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 | } |
117 | static void tftp_progress_done(void) | 117 | static 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 | |||
44 | config 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 | ||
44 | config DHCPD_LEASES_FILE | 58 | config 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 | ||
73 | config FEATURE_UDHCP_PORT | 87 | config 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 | |||
13 | struct host_info { | 17 | struct 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) |
40 | struct 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. */ | ||
129 | static 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. */ | ||
147 | static 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. */ |
162 | static char *base64enc_512(char buf[512], const char *str) | 150 | static 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 | ||
194 | static 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' */ |
183 | static 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 | |||
202 | static 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 | ||
221 | static void parse_url(char *src_url, struct host_info *h) | 223 | static 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 | ||
281 | static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/) | 284 | static 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 | ||
367 | static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa) | 359 | static 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 | ||
447 | static void NOINLINE retrieve_file_data(FILE *dfp, int output_fd) | 438 | static 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 | ||
529 | int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 559 | static void download_one_url(const char *url) |
530 | int 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 | |||
858 | int wget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
859 | int 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 | } |