Compare commits

..

4 Commits

Author SHA1 Message Date
Xavier Roche
87f178e65d Attribute the proxy port range to TCP, not RFC 3986
RFC 3986 defines port as *DIGIT with no range; the 1..65535 bound comes from
TCP's 16-bit port field.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-07-16 19:28:37 +02:00
Xavier Roche
b9b188d83b Reject an out-of-range -P proxy port instead of wrapping it
hts_parse_proxy read the port with sscanf(a, "%d", &p), which is signed
overflow UB past INT_MAX. glibc wraps rather than fails, so a garbage
port got through: -P 'http://host:99999999999999' parsed to port
276447231, while 'http://host:4294967296' happened to fail and fall back
to the default. 65536 was accepted as-is.

Parse with strtol and range-check to *DIGIT in 1..65535 (RFC 3986); an
out-of-range or malformed port now falls back to proxy_default_port,
exactly as an unparsable one already did.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
2026-07-16 18:58:36 +02:00
Xavier Roche
18bdc24d15 Document that PRs are squash-merged, not merged (#609)
Both files still describe the pre-July policy and tell contributors to polish
each commit message because the branch lands on master as-is. Under squash only
the PR title and description survive, so the advice pointed at the wrong thing.

Signed-off-by: Xavier Roche <roche@httrack.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 18:42:06 +02:00
Xavier Roche
0b772ec6ba Untrack the local CLAUDE.local.md symlink (#608)
It points at a path under my home directory and was committed by accident
in #556, so a fresh clone gets a dangling symlink. The file's own preamble
calls it untracked guidance for one checkout; ignore it like coucal and
httrack-windows already do.

Signed-off-by: Xavier Roche <roche@httrack.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 18:41:53 +02:00
4 changed files with 39 additions and 5 deletions

3
.gitignore vendored
View File

@@ -43,3 +43,6 @@ Makefile
# Python bytecode (tests/local-server.py).
__pycache__/
# Per-checkout Claude Code rules (symlink into a local sandbox).
/CLAUDE.local.md

View File

@@ -1 +0,0 @@
/home/roche/git/httrack-works/CLAUDE.httrack.local.md

View File

@@ -65,6 +65,7 @@ Please visit our Website: http://www.httrack.com
#endif /* _WIN32 */
#include <stdarg.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>
@@ -3747,6 +3748,20 @@ static int proxy_default_port(const char *arg) {
return hts_proxy_is_socks(arg) ? 1080 : 8080;
}
// port "a" of -P argument "arg": digits fitting TCP's 1..65535, else the scheme
// default. Not sscanf("%d"): past INT_MAX it wraps to a garbage port (#602)
static int parse_proxy_port(const char *a, const char *arg) {
char *end;
long p;
if (!isdigit((unsigned char) *a)) // strtol would eat a sign or leading space
return proxy_default_port(arg);
p = strtol(a, &end, 10);
if (*end != '\0' || p < 1 || p > 65535) // ERANGE lands out of range too
return proxy_default_port(arg);
return (int) p;
}
void hts_parse_proxy(const char *arg, char *name, size_t name_size, int *port) {
const char *authority = strstr(arg, "://");
const char *a;
@@ -3762,10 +3777,7 @@ void hts_parse_proxy(const char *arg, char *name, size_t name_size, int *port) {
while (a > authority && a[-1] != ':' && a[-1] != '@' && a[-1] != ']')
a--;
if (a > authority && a[-1] == ':') {
int p = -1;
sscanf(a, "%d", &p);
*port = (p > 0) ? p : proxy_default_port(arg);
*port = parse_proxy_port(a, arg);
namelen = (size_t) (a - 1 - arg);
} else {
*port = proxy_default_port(arg);

View File

@@ -71,3 +71,23 @@ p 'http://a]b:c@host' 'name=http://a]b:c@host port=8080 host=host kind=http'
# a lone ':' must not underflow the backward scan
p ':' 'name= port=8080 host= kind=http'
# a port is *DIGIT in 1..65535, else the scheme default: sscanf("%d") used to
# wrap past INT_MAX and hand back a garbage port (#602)
p 'http://host:65535' 'name=http://host port=65535 host=host kind=http'
p 'http://host:1' 'name=http://host port=1 host=host kind=http'
p 'http://host:65536' 'name=http://host port=8080 host=host kind=http'
p 'http://host:2147483648' 'name=http://host port=8080 host=host kind=http'
p 'http://host:4294967296' 'name=http://host port=8080 host=host kind=http'
p 'http://host:99999999999999' 'name=http://host port=8080 host=host kind=http'
# discriminating: this one wrapped to a plausible 80, so range-checking the
# sscanf result instead of rejecting the overflow still passes everything above
p 'http://host:4294967376' 'name=http://host port=8080 host=host kind=http'
p 'http://host:999999999999999999999999999999' 'name=http://host port=8080 host=host kind=http'
p 'http://host:0' 'name=http://host port=8080 host=host kind=http'
p 'http://host:-1' 'name=http://host port=8080 host=host kind=http'
p 'http://host:80x' 'name=http://host port=8080 host=host kind=http'
p 'http://host: 80' 'name=http://host port=8080 host=host kind=http'
p 'http://host:' 'name=http://host port=8080 host=host kind=http'
# the fallback is the scheme's own default, not a hardcoded 8080
p 'socks5://host:99999999999999' 'name=socks5://host port=1080 host=host kind=socks'