Viewing wget 2.20 Through Code.
11月24日(現地時間)にwget 2.20が公開されました。今回のアップデートはコアとなる機能で大幅な改善がされていますので、紹介していけたらと思います。
変更があった箇所について、以下のURLからチェックすることができます
https://gitlab.com/gnuwget/wget2/-/blob/master/NEWS?ref_type=heads
今回どのようなアップデートがあったのか簡単にまとめました。参考になれば幸いです。
セキュリティ関係
Don’t log URI userinfo to logs(https://gitlab.com/gnuwget/wget2/-/commit/dc8966d9060533264501bcd269fec4b2dc443df2)
構造体にsafe_uriが追加され、これはURLからのユーザー情報を含まないようになっています。
wget_iri *wget_iri_parse(const char *url, const char *encoding)
{
// 認証情報がある場合
if (iri->userinfo) {
iri->safe_uri = create_safe_uri(iri);
} else {
// userinfoがない場合は元のURIをそのまま使用
iri->safe_uri = iri->uri;
}
return iri;
wget https://user:password@example.com/file
これによってユーザーとパスワードを入力してファイルを取得するときに認証情報がログファイルに残らないようになりました。セキュリティ監査時の指摘事項が減少することになります。
Disable explicit OCSP requests by default(https://gitlab.com/gnuwget/wget2/-/commit/c341fcd1dfd57b3cf5a1f5acb84784571fff3a20)
.check_certificate = 1,
.check_hostname = 1,
#ifdef WITH_OCSP
// デフォルトで無効に変更
// .ocsp = 1, 変更前
.ocsp = 0, // 変更後
.ocsp_stapling = 1,
#endif
.ca_type = WGET_SSL_X509_FMT_PEM,
Fix segfault when OCSP response is missing(https://gitlab.com/gnuwget/wget2/-/commit/c556a3226aca0e99191b52218117b7967889a9bf)
レスポンスオブジェクトが空でないことが確認される部分が追加されました。
// 変更前
certid = OCSP_cert_to_id(EVP_sha1(), subject_cert, issuer_cert);
if (!(ocspreq = send_ocsp_request(ocsp_uri, certid, &resp)))
return -1;
// 変更後
certid = OCSP_cert_to_id(EVP_sha1(), subject_cert, issuer_cert);
if (!(ocspreq = send_ocsp_request(ocsp_uri, certid, &resp)) || !resp || !resp->body)
return -1;
Fix OCSP verification of first intermediate certificate(https://gitlab.com/gnuwget/wget2/-/commit/53a8a88e8479fca04fb17f923b0f40781ee6a253)
// if (config.ocsp && it > nvalid) {
// オフバイワンエラーの修正
if (config.ocsp && it > = nvalid) {
char fingerprint[64 * 2 +1];
int revoked;
OCSP関連をまとめますと
・デフォルトでは明示的なOCSPが無効になった
・OCSPレスポンス欠落時のクラッシュを修正
・OCSPの中間証明書の検証プロセスを改善
という感じです。
Disable TCP Fast Open by default(https://gitlab.com/gnuwget/wget2/-/commit/7a945d31aeb34fc73cf86a494673ae97e069d84d)
.max_redirect = 20,
.max_threads = 5,
.dns_caching = 1,
// .tcp_fastopen = 1, デフォルトで1から0に変更
// we use 'Wget' here for compatibility, see https://github.com/rockdaboot/wget2/issues/314
.user_agent = "Wget/"PACKAGE_VERSION,
.verbose = 1,
TCP Fast Open(TFO)がデフォルトで無効になりました。TFOは第三者から追跡に利用される可能性があったこと、TFOをサポートしていないミドルボックスとの問題から、セキュリティーとプライバシーを考慮した結果、無効になったみたいです。
Allow option –no-tcp-fastopen to work on Linux kernels >= 4.11(https://gitlab.com/gnuwget/wget2/-/commit/7929bf887c69ffdcbdfb525825bffba4c9e5d6e8)
--no-tcp-fastopen
オプションをLinuxカーネル4.11以降でも機能するよう修正されました。ユーザーに選択の余地を残している感じですね。
Limit cases where methods are redirected to GET(https://gitlab.com/gnuwget/wget2/-/commit/329d1282caa9ae58105a6b6832138050c492dc28)
リダイレクト時のメソッド変更に関する厳密な制御が行われるようになりました。301,302,303に関してはHTTPリダイレクトの自動変更を制限されました。これによって不適切なリダイレクトによるメソッド変更が防ぐようになります。
if (resp->code / 100 == 3 && resp->code != 307) job->redirect_get = 1; //変更前
// 変更後
if (!wget_strcasecmp_ascii(resp->req->method, "POST"))
{
if (resp->code == 301 || resp->code == 302 || resp->code == 303)
job->redirect_get = 1;
}
バグの修正
Don’t truncate file when -c and -O are combined(https://gitlab.com/gnuwget/wget2/-/commit/1cb578e3e9e86b32f9a5157a598d8ff0de44bd3c)
-c(続きからダウンロード)と-O(ファイル名指定)コマンドを合わせて使った時にファイルが切り詰められる問題が修正されました。ファイル名指定の際に、途中再開が可能になりました。
// 変更前
} else {
// 変更後
// 継続ダウンロードでない場合は切り捨てるように変更
} else if (!config.continue_download) {
int fd = open(config.output_document, O_WRONLY | O_TRUNC | O_BINARY);
if (fd ! = -1)
Fix downloading multiple files via HTTP/2(https://gitlab.com/gnuwget/wget2/-/commit/ec27488feadd44b5e126592bc18ff2441f8cae5a)
HTTP/2での複数ダウンロードの修正という直訳ですが、従来までは単一のTCP接続でストリームの状態管理が不適切であることが原因で、切断や混線が起きていました。コミット履歴をみますと、以下の部分でそれが改善されているのがわかります。
struct http2_stream_context {
wget_http_connection *conn;
wget_http_response *resp;
wget_decompressor *decompressor;
};
*conn
→ HTTP/2接続の参照を保持し、各ストリームがどの接続に属しているかを追跡 *resp
→ストリームごとのレスポンスデータを個別に管理 *decompressor
→圧縮されたレスポンスデータの解凍状態を各ストリームで独立して管理
これによって各ストリームごとの適切な管理が可能となり、レスポンス処理に関して適切な追跡が出来るようになりました。
Fix redirections with –no-parent(https://gitlab.com/gnuwget/wget2/-/commit/55a4c145c80325b2fb0b1fb3768f31094154e5d3)
–no-parentをつけたときに親URLのチェックが甘くて有効なURLもスキップされていたということですが、今回修正されたとのことです。
if (config.recursive && !config.parent && !(flags & URL_FLG_REQUISITE)) {
// 親ディレクトリへの移動をデフォルトで制限
bool ok = false;
// 少なくとも1つの親ディレクトリと一致するかチェック
for (int it = 0; it < wget_vector_size(parents); it++) {
wget_iri *parent = wget_vector_get(parents, it);
if (!wget_strcmp(parent->host, iri->host)) {
if (!parent->dirlen || !wget_strncmp(parent->path, iri->path, parent->dirlen)) {
ok = true;
break;
}
}
}
if (!ok) {
info_printf(_("URL '%s' not followed (parent ascending not allowed)\n"), url);
goto out;
}
}
Fix –no-parent for denormalized paths(https://gitlab.com/gnuwget/wget2/-/commit/9aeab55d09f9df833bca4467b0a209cea2901ede)
wget_iri_parse()
関数内で、normalize_path()
関数を呼び出してIRIのパス部分を正規化するようになりました。
c = *s;
if (c) *s++ = 0;
wget_iri_unescape_inline((char *)iri->path);
normalize_path((char *)iri->path); // 追加
}
Fix status 8 for failed redirection of robots.txt(https://gitlab.com/gnuwget/wget2/-/commit/2b1f266ca639b7712a973c1512a2611d5fce7930)
robots.txtのエラーが302リダイレクトで、その後のリクエストが404エラーを返す状況で、wget2が不適切にステータスコード8で終了していたという問題です。本来、robots.txtの404エラーは無視されるべき動作でしたが、リダイレクトが含まれる場合に適切に処理されていなかったということです。新しく以下の行が追加されました。
new_job->robotstxt = job->robotstxt;
これによってリダイレクト時に元のジョブから新しいジョブにrobotstxt
フラグをコピーするようになりました。リダイレクトされたリクエストもrobots.txtとして認識されるようになり、404エラーも適切に処理がされるようになります。
Fix IPv6 address representation(https://gitlab.com/gnuwget/wget2/-/commit/ff881ed20182950accf77cf70bcaf51ec75d1a87)
if (sscanf(buf, "%63[0-9.:] %255[a-zA-Z0-9.-]", ip, name) != 2) // 変更前
if (sscanf(buf, "%63s %255s", ip, name) != 2) // 変更後
文字クラス [0-9.:]
を前回まで使っていましたが、IPv6 アドレスの一部の有効な文字(16進数のアルファベットや、[]など)は受け付けませんでした。以下のように修正されました。
※入力63文字、ホスト名255文字という制限は引き続き継続です。
Fix –dns-cache-preload for IPv6 (https://gitlab.com/gnuwget/wget2/-/commit/ff881ed20182950accf77cf70bcaf51ec75d1a87)
上記と同様の修正です。
Fix –restrict-file-names to be backwards compatible with wget 1.x(https://gitlab.com/gnuwget/wget2/-/commit/284954553613f75e57ef107ceaa06ae4d9dd8c59)
–restrict-file-namesがwget1.xと同様の処理を行えるように改善したということです。
--restrict-file-names=windows,ascii,lowercase // 複数のオプションサポートが可能に
Several improvements to the WolfSSL code(https://gitlab.com/gnuwget/wget2/-/commit/1d6632a31c5fbec2145762c5fffcf31af313e47a)
セミコロンの追加です。
//変更前
XFREE(subject, 0, DYNAMIC_TYPE_OPENSSL)
XFREE(issuer, 0, DYNAMIC_TYPE_OPENSSL)
//変更後
XFREE(subject, 0, DYNAMIC_TYPE_OPENSSL);
XFREE(issuer, 0, DYNAMIC_TYPE_OPENSSL);
機能追加
Support connecting with HTTP/1.0 proxies(https://gitlab.com/gnuwget/wget2/-/commit/f5344eb415a8b221e1b887d02b31090c6459bfd8)
従来まではHTTP/1.1のみを許可していましたが、今回からHTTP/1.0も許可されるようになりました。これによってさまざまなプロキシサーバーとの互換性が向上するようになります。
if (wget_strncasecmp_ascii(sbuf, "HTTP/1.1 200", 12)) { // 変更前
if (wget_strncasecmp_ascii(sbuf, "HTTP/1.1 200", 12) && wget_strncasecmp_ascii(sbuf, "HTTP/1.0 200", 12)) { // 変更後
Ignore 1xx HTTP responses for HTTP/1.1(https://gitlab.com/gnuwget/wget2/-/commit/fa638f597c3eefa3cc87493debe4cf075aed3c55)
1xx 応答を効果的に無視し、次の応答を待つ処理が実装されました。
skip_1xx:
if (nread < 4)
continue;
if (nread - nbytes <= 4)
if (H_10X(resp->code)) {
wget_http_free_response(&resp);
p += 4;
// 現在まで読んだバイト数を計算
nbytes = nread -= (p - buf);
// 残りのデータをバッファの先頭に移動
memmove(buf, p, nread + 1);
goto skip_1xx; // 中間応答を無視、ボディは想定しない
}
Fix ignoring connect timeout (regression)(https://gitlab.com/gnuwget/wget2/-/commit/21f41932af46faa9a144b7025d99270353021a61)
struct timeval tv = {
// タイムアウトを秒とマイクロに分割
.tv_sec = tcp->connect_timeout/1000,
.tv_usec = tcp->connect_timeout % 1000 * 1000
};
// ソケットオプション(SO_SNDTIMEO)の追加
if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1)
error_printf(_("Failed to set socket option SO_SNDTIMEO\n"));
}
修正となっていますが、送信操作時のタイムアウトが新しくわかりやすいように追加されたという感じです。これによって接続が長時間ブロックされることを防止するようになりました。
Accept –progress=dot:… for backwards compatibility(https://gitlab.com/gnuwget/wget2/-/commit/e8f1e99c96a8303421e66b0feda1651a11c8b250)
–progressオプション(進捗表示)の改善がされました。wget_strncasecmp_ascii
を使用して、オプションの前方一致チェックを行ったり、val[3] == ':' || val[3] == 0
により、オプション後にコロンや終端文字がある場合も許容するようになりました。
※dot
オプションは現時点では実装されていないため、情報メッセージを表示するだけです。
また、config.force_progress
フラグを設定するロジックも追加されています。
if (!wget_strcasecmp_ascii(val, "none"))
*((char *)opt->var) = PROGRESS_TYPE_NONE;
// else if (!wget_strncasecmp_ascii(val, "bar", 3)) { // 変更前
else if (!wget_strncasecmp_ascii(val, "bar", 3) && (val[3] == ':' || val[3] == 0)) { // 変更後
*((char *)opt->var) = PROGRESS_TYPE_BAR;
// if (!wget_strncasecmp_ascii(val+3, ":force", 6) || !wget_strncasecmp_ascii(val+3, ":noscroll:force", 15)) { // 変更前
if (!wget_strncasecmp_ascii(val+4, "force", 5) || !wget_strncasecmp_ascii(val+4, "noscroll:force", 14)) { // 変更後
config.force_progress = true;
}
// } else if (!wget_strcasecmp_ascii(val, "dot")) { // 変更前
} else if (!wget_strncasecmp_ascii(val, "dot", 3) && (val[3] == ':' || val[3] == 0)) { // 変更後
// Wget compatibility, whether want to support 'dot' depends on user feedback.
info_printf(_("Progress type '%s' ignored. It is not implemented yet\n"), val);
} else {
これによって異なる形式の進捗表示オプションに対応するようになりました。
Fix possible deadlock when combining –no-clobber and –no-parent(https://gitlab.com/gnuwget/wget2/-/commit/8ebd0a25f068c34209dd42c6fea4db6e3b381626)
マルチスレッド環境でのURLの重複チェックとロック処理、再帰的なダウンロードの条件が改善されました。
if (wget_hashmap_put(known_urls, wget_strmemdup(buf.data, buf.length), NULL) == 0) { // 変更前
// 変更後
wget_thread_mutex_lock(known_urls_mutex);
int rc = wget_hashmap_put(known_urls, wget_strmemdup(buf.data, buf.length), NULL);
wget_thread_mutex_unlock(known_urls_mutex);
if (rc == 0) {
if (config.recursive && (!config.level || (job && job->level < config.level + config.page_requisites))){ // 変更前
if (config.recursive && (!config.level || !job || (job && job->level < config.level + config.page_requisites))) { // 変更後
この変更によって潜在的なデッドロック状態のリスクを軽減しています。
Fix xattr reading of user.mime_type(https://gitlab.com/gnuwget/wget2/-/commit/ac9c84b3bab60a0cd1100ac6f189fc526694e95c)
MIME タイプ読み取りのロジックが簡略化され、”user.mime_type” 属性に焦点を当てる形に変更されました。
if (read_xattr_metadata("user.mimetype", _mimetype, sizeof(_mimetype), fd) < 0) // 変更前
if (read_xattr_metadata("user.mime_type", _mimetype, sizeof(_mimetype), fd) < 0) // 変更後
Fix robots.txt parser(https://gitlab.com/gnuwget/wget2/-/commit/07b15e71f4d72c53fb10fdeb28a188b94b6c35ac)
robots.txtのロジックが大幅に改善されました。これによって様々な形式の robots.txt ファイルに対応できるようになりました。
static bool parse_record_field(const char **data, const char *field, size_t field_length)
{
advance_ws(data);
if (wget_strncasecmp_ascii(*data, field, field_length))
return false;
*data += field_length;
advance_ws(data);
if (**data ! = ':')
return false;
*data += 1;
advance_ws(data);
return true;
}
Add fetchmail compatibility for user/password in .netrc(https://gitlab.com/gnuwget/wget2/-/commit/ae24f83fd06d8834188957830eeb043c5b7d5cc9)
.netrc ファイルの互換性を向上するように以下のコードが変更されました。
// 変更前
else if (!strcmp(key, "login")) {
}
else if (!strcmp(key, "password")) {
}
// 変更後
else if (!strcmp(key, "login") || !strcmp(key, "user")) {
// "user" is for fetchmail compatibility
if (!netrc.login)
netrc.login = wget_strmemdup(p, linep - p);
}
else if (!strcmp(key, "password") || !strcmp(key, "passwd")) {
// "passwd" is for fetchmail compatibility
if (!netrc.password) {
if (!escaped)
netrc.password = wget_strmemdup(p, linep - p);
}
}
ログイン情報やパスワード情報の読み取りを拡張した形になっています。
Improve suport for non-standard cookie timestamps(https://gitlab.com/gnuwget/wget2/-/commit/7bf93ff6c64520e2931b1c79663851b188ee2016)
従来までは非標準だった日付形式に対応ができるようになりました。
具体的には、Sun Nov 26 2023 21:24:47 という形式ですね。
else if (sscanf(s, " %*s %3s %2d %4d %2d:%2d:%2d", // non-standard: Sun Nov 26 2023 21:24:47
mname, &day, &year, &hour, &min, &sec) == 6) {
}
Add libproxy support(https://gitlab.com/gnuwget/wget2/-/commit/1a886595e69f54247c70a1e553676407fc8028c7)
libproxy サポートを追加しました(直訳ですみません)。
具体的には、
configure.acに–enable-libproxyを追加
# libproxy support
with_libproxy=no
AC_ARG_ENABLE(libproxy,
[ --enable-libproxy libproxy support for system wide proxy configuration])
AS_IF([test "${enable_libproxy}" = "yes"], [
with_libproxy=yes
PKG_CHECK_MODULES([LIBPROXY], [libproxy-1.0], [
LIBS="$LIBPROXY_LIBS $LIBS"
CFLAGS="$LIBPROXY_CFLAGS $CFLAGS"
AC_DEFINE([HAVE_LIBPROXY], [1], [Define if using libproxy.])
])
])
libget/http.cにシステム全体のプロキシ設定を取得するコードを追加
{
pxProxyFactory *pf = px_proxy_factory_new();
if (pf) {
char **proxies = px_proxy_factory_get_proxies(pf, iri->uri);
if (proxies) {
if (proxies[0]) {
if (strcmp (proxies[0], "direct://") != 0) {
wget_iri *proxy_iri = wget_iri_parse(proxies[0], "utf-8");
host = strdup(proxy_iri->host);
port = proxy_iri->port;
if (proxy_iri->scheme == WGET_IRI_SCHEME_HTTP) {
ssl = false;
conn->proxied = 1;
} else {
ssl = true;
need_connect = true;
}
wget_iri_free(&proxy_iri);
}
}
px_proxy_factory_free_proxies(proxies);
}
px_proxy_factory_free (pf);
}
}
これによってlibproxy を使用して、接続先に応じて最適なプロキシを自動的に選択できるようになりました。
その他
Add instruction on how to cross-build wget2.exe via docke(https://gitlab.com/gnuwget/wget2/-/commit/045976cf8f046477efea081d4ea9e336cb5ce15b)
windowsのクロスコンパイル用のドキュメントと手順が追加されました。
詳細はリンクを参照してもらうと助かります。
要約すると、
1.Dockerfileを使用してクロスコンパイル環境をセットアップ
2. Windows用の静的バイナリ (wget2.exe
) をビルド
3. 生成されたバイナリをホストマシンにコピー
※オプションで、デバッグシンボルを削除したり、実行可能ファイルを圧縮したりするステップを提供している
という感じです。
Don’t request preferred mime type for single file downloads
Slightly improved compatibility with LibreSSL
この2項目については、コミットを探しましたがそれらしきものがありませんでした。今後更新予定かもしれません
まとめ
今回のリリースは技術改善以上にセキュリティ強化として、ユーザー情報の保護、OCSPの制御、プライバシーを考慮したTCP設定、そしてより安全なリダイレクト処理が追加・変更されました。
URIのユーザー情報をログに記録しないことで認証情報の漏洩リスクを低減し、TCP Fast Openをデフォルトで無効化することでネットワークトラッキングの可能性を抑制しています。また、HTTP/2での複数ファイルダウンロード時の安定性向上や、robots.txtの解析改善など、実践的なセキュリティと信頼性の強化に焦点を当てているように感じました。
おわり