狼言狼语

「狼言狼语」

阅读来自「狼言狼语」作者们的最新文章

from liuzhen932

Alright, in this article, we're going to dive into the world of email addresses.

Most developers think they understand email addresses. After all, what's complicated about john@example.com? But dig deeper into the RFC specifications, and you'll discover a rabbit hole of edge cases, surprising rules, and implementation quirks that would make even seasoned engineers do a double-take.

The Deceptive Simplicity

At first glance, email validation seems straightforward. You might write a regex, check for an @ symbol, maybe validate the domain format, and call it a day. But the reality is far more nuanced. The email address specification, primarily governed by RFC 5322 and RFC 5321[^1], contains layers of complexity that most systems simply ignore or handle incorrectly.

Consider this seemingly innocent address: user+shopping@gmail.com. It's valid, widely supported, and useful for filtering emails. But what about "very.VERY.\"very@\\ \"very\".unusual"@strange.example? Believe it or not, this monstrosity is also perfectly valid according to the specifications[^2].

Breaking Down the Anatomy

An email address consists of two parts separated by the @ symbol: the local-part and the domain. The local-part can be up to 64 octets, while the domain can be up to 255 octets[^3]. But within these constraints lies a world of possibilities that most developers never encounter.

The Local-Part Mysteries

The local-part—the bit before the @ symbol—has two forms: quoted and unquoted. Unquoted local-parts can contain letters, digits, and a specific set of special characters: !#$%&'*+-/=?^_{|}~`. The period is allowed but with restrictions—it can't be first, last, or consecutive.

But here's where it gets interesting. What happens when you add quotes? Suddenly, you can include almost any ASCII character. Want to have a space in your email address? "john doe"@example.com is valid. Need to include a comma? "user,name"@example.com works too.

Take this example: normal(wtf is this?)@example.com. The parenthetical comment is completely ignored by the email system, making it equivalent to normal@example.com. Comments can appear at the beginning, middle, or end of the local-part, and they're simply stripped away during processing.

Domain Surprises

Most people expect domains to look like example.com, but the specification allows for more exotic forms. IP address literals are valid: user@[192.168.1.1] will work, though it's rarely seen outside of spam. IPv6 addresses are also supported: admin@[2001:db8::1].

Perhaps more surprisingly, domains don't always need a top-level domain. admin@example is technically valid, though ICANN strongly discourages such “dotless” domains[^4].

The Emoji Revolution

With the advent of internationalized email addresses (RFC 6530[^5]), we've entered a new era. Unicode characters are now permissible in both the local-part and domain, leading to addresses like test@россия.рф or even 👋@example.com.

But the real mind-bender comes with RFC 6532, which allows Unicode in domain literals. This means user@[💩] is theoretically valid. While most mail servers won't handle this gracefully, the specification doesn't prohibit it[^6].

The Quote Conundrum

Here's something that breaks most people's mental model: ""@example.com is valid. An empty quoted string in the local-part is perfectly acceptable, even though an empty unquoted local-part is not. The distinction between @example.com (invalid) and ""@example.com (valid) illustrates the subtle complexities in the specification.

Even more bizarre, you can have technical shell commands as email addresses. ":(){:|:&};:"@example.com is valid—it's a fork bomb wrapped in quotes. The quotes prevent interpretation, making it just another string of characters[^7].

Case Sensitivity: The Great Debate

The specification states that local-parts MUST be treated as case-sensitive[^8]. This means John@example.com and john@example.com are technically different addresses. However, the same RFC also urges that receiving hosts should deliver messages in a case-independent manner. This contradiction has led to inconsistent implementations across different mail systems.

Gmail takes this further by ignoring periods in the local-part entirely. john.smith@gmail.com, johnsmith@gmail.com, and j.o.h.n.s.m.i.t.h@gmail.com all deliver to the same inbox[^9].

Plus Addressing: The Power User's Secret

Many mail servers support sub-addressing, where everything after a plus sign in the local-part is ignored for delivery purposes. user+newsletter@example.com gets delivered to user@example.com, but the tag remains visible in the headers, allowing for powerful filtering rules[^10].

This feature, supported by Gmail, Outlook, Fastmail, and others, is invaluable for tracking email sources and creating disposable addresses. Yet many web forms reject these addresses as “invalid,” demonstrating the gap between specification and implementation.

Implementation Reality vs. Specification

While the RFCs define what's technically possible, real-world implementations are far more restrictive. Windows Live Hotmail, for instance, only allows alphanumeric characters, periods, underscores, and hyphens[^11]. Many web forms implement overly strict validation that rejects perfectly valid addresses.

This creates a frustrating situation where an address might be valid according to the specification, deliverable by some mail servers, but rejected by the very websites that need to send confirmation emails.

The Postmaster Exception

There's one special local-part that deserves mention: postmaster. This address is case-insensitive and should be forwarded to the domain's email administrator[^12]. Every domain that accepts email must provide a working postmaster address, making it a crucial administrative contact point.

International Considerations

The push for internationalized email addresses has opened up possibilities for users worldwide to have addresses in their native scripts. Chinese users can have addresses like 我買@屋企.香港, while Russian users might prefer медведь@с-балалайкой.рф[^13].

However, support remains patchy. While the specifications exist (RFCs 6530-6533), many legacy systems and poorly implemented validators still struggle with non-ASCII characters.

The Validation Nightmare

All this complexity makes email validation surprisingly difficult. A truly compliant validator must handle quoted strings, comments, internationalization, IP literals, and numerous edge cases. Most developers opt for simplified validation that covers 99% of real-world cases while rejecting some technically valid addresses.

The common advice is to send a verification email rather than relying solely on format validation. If the user receives and responds to the email, the address is valid for all practical purposes.

Looking Forward

Email addresses have evolved far beyond their original simple design. While the core concept remains unchanged, the specification has grown to accommodate global needs, security concerns, and changing technology landscapes.

Understanding these complexities isn't just academic—it affects how we build systems, validate user input, and handle edge cases. The next time you implement email validation, remember that behind every email address lies a specification rich with history, compromise, and surprising flexibility.

The humble email address, something we use dozens of times daily, contains multitudes of complexity hidden beneath its familiar facade. In a world where we take digital communication for granted, it's worth appreciating the engineering effort required to make something so complex appear so simple.

References

[^1]: RFC 5322: Internet Message Format; RFC 5321: Simple Mail Transfer Protocol

[^2]: Example from RFC 3696 demonstrating complex but valid email address syntax

[^3]: RFC 5321, Section 4.5.3.1: Size limits and minimums for email address components

[^4]: ICANN announcement 2013-08-30: New gTLD Dotless Domain Names Prohibited

[^5]: RFC 6530: Overview and Framework for Internationalized Email

[^6]: RFC 6532: Internationalized Email Headers, allowing Unicode in all email components

[^7]: Quoted strings in RFC 5322 allow most ASCII characters when properly escaped

[^8]: RFC 5321, Section 2.4: Local-part case sensitivity requirements

[^9]: Gmail support documentation on address handling and dot notation

[^10]: RFC 5233: Sieve Email Filtering: Subaddress Extension specification

[^11]: Windows Live Hotmail registration requirements (archived documentation)

[^12]: RFC 5321, Section 4.5.1: Required postmaster address specification

[^13]: RFC 6530-6533: Internationalized email address examples and implementation

  • e-mail.wtf – Some of the examples in this article were inspired by
 
Read more...

from liuzhen932

Tired of bloated CAPTCHAs that track users, slow down websites, or force you into restrictive ecosystems? Cap.js is the modern alternative—lightweight, open-source, and built on proof-of-work (PoW) to stop bots without sacrificing privacy or performance.

Cap.js redefines what a CAPTCHA can be.

Github • Website • Quickstart • Demo

Why I those Cap.js

✅ 250x Smaller, 100% Faster

The core widget clocks in at 12KB, making it 250x smaller than hCaptcha. No intrusive puzzles—just a quick computational challenge that’s easy for humans but exhausting for bots.

🔒 Privacy by Design

Zero tracking, fingerprinting, or data collection. Unlike competitors, Cap.js relies on math, not surveillance, to verify users.

🔧 Unmatched Flexibility

Deploy with any language/framework via a simple REST API. Customize the backend and frontend to fit your stack.

Feature comparison

Feature comparison table

Alternatives

Cap is a modern alternative to: – reCAPTCHAhCaptchaCloudflare Turnstile


FAQs

What is Cap and what does it offer?

Cap is a lightweight, modern open-source CAPTCHA alternative using SHA-256 proof-of-work. It's fast, private, and extremely simple to integrate.

Is Cap completely free?

Yes, Cap.js is completely free. Cap is an open source project that you can deploy on your own server (as a standalone server) or integrate into your backend application.

How difficult is it to integrate Cap.js?

Integrating Cap is very simple! All you need to do is import a JavaScript file into your client and set up the widget component to get started! For more information, please refer to the Quickstart.

Conclusion

Cap.js isn’t just another tool—it’s a shift toward privacy-first, developer-friendly security.

Based on my experience over the past few days, Cap.js is very easy to integrate. It can be tested and deployed with many of my projects and frameworks in just ten minutes.

I have recommended Cap.js to my friends and also shared it with friends on the Fediverse (like you!) through articles/posts.

 
Read more...

from liuzhen932

When analyzing network paths using the traceroute command, you might encounter the symbol !H in the output. This notation indicates a specific type of error encountered during the tracing process.

Explanation of “!H”

The !H symbol signifies “Host Unreachable”, meaning the network device (router or host) along the path could not forward the probe packet to the target host. This occurs when an intermediate router or the destination device itself returns an ICMP Host Unreachable message (ICMP Type 3, Code 1).

Context of “!H”

  • Traceroute works by sending packets with incrementing TTL (Time-to-Live) values to identify hops between your device and a destination.
  • When a hop cannot reach the next device in the path, it responds with an ICMP error, which traceroute interprets and displays.
  • !H specifically highlights that the target host itself is unreachable at that point in the path.
  • If almost all the probes result in some kind of unreachable, traceroute will give up and exit.

While !H is a common error, traceroute uses other symbols to denote different issues. Here are key examples:

  1. !N: Network Unreachable

    • Indicates the router cannot route to the target network.
  2. !X: Communication Administratively Prohibited

    • A firewall or router explicitly blocked the packet.
  3. !P: Port Unreachable

    • The destination port is closed or unreachable.
  4. !S: Source Quench

    • The router temporarily halted packet forwarding due to congestion.
  5. !D: Destination Unreachable

    • A generic error for unreachable destinations.
  6. !P: Protocol Unreachable

    • The target host does not support the protocol (e.g., UDP) used.

In summary, !H in traceroute alerts you to a host-level connectivity issue at a specific hop, aiding in diagnosing network path failures.

 
Read more...

from liuzhen932

In network administration and DNS management, it's often necessary to convert IPv6 networks into their corresponding reverse DNS zones. This process is crucial for reverse lookups, which allow you to map IP addresses back to domain names. In this article, we'll walk through the process of converting IPv6 networks to reverse DNS zones using JavaScript.

Understanding Reverse DNS Zones

Reverse DNS zones are used to map IP addresses to domain names. For IPv6, the reverse zone format is different from IPv4. Instead of using decimal numbers, IPv6 uses hexadecimal notation, and the address is reversed. For example:

  • IPv6 Address: 2601:9:4f00:b7c::/64
  • Reverse DNS Zone: c.7.b.0.0.0.f.4.9.0.0.0.1.0.6.2.ip6.arpa

The Code

Below is the JavaScript code that performs the conversion:

/**
 * Generate IPv6 Reverse DNS Zone Name
 * @param {string} network - IPv6 network address (format: `address/prefix`)
 * @returns {string} Reverse DNS zone name (format: `<reversed_hex>.ip6.arpa`)
 * @example
 * v6ReverseZone('2601:9:4f00:b7c::/64') // => 'c.7.b.0.0.0.7.b.0.0.f.4.0.0.9.0.1.6.2.ip6.arpa'
 */
function v6ReverseZone(network) {
  // Split address and prefix
  const [address, prefix] = network.split('/');
  // Split IPv6 address into segments
  const parts = address.split(':');
  // Find the position of the double colon (::)
  const pos = parts.indexOf('');
  // Expand IPv6 address into 32-character hexadecimal string
  const expanded = (
    pos === -1
      ? parts.map(p => p.padStart(4, '0')).join('')
      : [
          ...parts.slice(0, pos).map(p => p.padStart(4, '0')),
          ...Array(8 - (parts.length - 1)).fill('0000'),
          ...parts.slice(pos + 1).map(p => p.padStart(4, '0'))
        ].join('')
  );
  // Calculate required hexadecimal length
  const hexLength = +prefix / 4;
  // Slice hexadecimal part according to prefix length
  const hexPart = expanded.slice(0, hexLength);
  // Split into individual characters, reverse the order, and join with dots
  const reversed = hexPart.split('').reverse().join('.');
  return `${reversed}.ip6.arpa`; // Construct the final domain name
}

// Test cases
console.log(v6ReverseZone('2601:9:4f00:b7c::/64'));

Conclusion

Converting IPv6 networks to reverse DNS zones is an essential task in network management. The provided JavaScript code simplifies this process by handling the expansion of IPv6 addresses, reversing the necessary segments, and formatting them correctly. This script can be integrated into larger applications or used as a standalone utility for DNS management tasks.

 
阅读更多

from liuzhen932

在搜索引擎优化(SEO)的世界里,及时让搜索引擎发现新内容至关重要。Bing 的 IndexNow 协议 提供了一个快速提交网页的通道,但手动操作效率太低。这篇博客将带你看懂一个自动化工具的实现原理,并手把手教你如何用它提升网站收录效率。

0x00 前提准备

const axios = require('axios');
const { parseString } = require('xml2js');

我们使用 axios 库执行 HTTP 请求来下载 sitemap 文件,xml2js 库则负责将 XML 数据解析为 JavaScript 对象。axios 像是处理网络请求的瑞士军刀,而 xml2js 则像一把精密的螺丝刀,能将复杂的 XML 结构拆解成易于操作的对象形式。

函数参数与核心流程

async function submitSitemapToBing(sitemapUrl, apiKey) { ... }

开发者小贴士:async/await 语法让异步代码具备同步可读性,但必须通过 try/catch 块妥善处理可能出现的错误。

0x01 下载并验证 Sitemap

const response = await axios.get(sitemapUrl);
if (response.status !== 200) {
throw new Error(`下载失败,状态码:${response.status}`);
}

这段代码首先发送 GET 请求获取 sitemap 文件,同时检查 HTTP 状态码。若返回非 200 状态码(如 404 或 500),会立即抛出错误终止流程,避免后续无效操作。

0x02 解析 XML 提取 URL 列表

parseString(response.data, (err, result) => {
const urlElements = result.urlset?.url;
if (urlElements) {
urls.push(...urlElements.map(urlObj => urlObj.loc[0]));
}
});

此处使用可选链操作符 ?. 防止因缺失 urlset 根节点导致的空引用错误。若 sitemap 格式不标准(如缺少必要节点)会导致解析失败,建议添加详细错误日志记录以便排查问题。

0x03 构建提交请求体

const body = JSON.stringify({
host: new URL(sitemapUrl).hostname,
key: apiKey,
keyLocation: `https://${hostname}/${apiKey}.txt`,
urlList: urls
});

Bing 的身份验证机制要求:key 是你的 API 密钥,keyLocation 需指向公开可访问的密钥验证文件(如 https://example.com/your-api-key.txt)。该文件内容必须与 key 参数完全一致,且必须提前部署完成,否则会导致 403 错误。

0x04 发送请求并处理响应

const postResponse = await axios.post(
'https://api.indexnow.org/IndexNow',
body,
{ headers: { 'Content-Type': 'application/json' } }
);

发送 POST 请求后,200 状态码表示提交成功,4xx 状态码通常表示客户端错误(如密钥无效或验证文件不可达),5xx 状态码则提示服务器端问题,建议稍后重试。

完整代码与实战指南

// 需要安装依赖:npm install axios xml2js
const axios = require('axios');
const { parseString } = require('xml2js');

async function submitSitemapToBing(sitemapUrl, apiKey) {
  try {
    // 下载 sitemap
    const response = await axios.get(sitemapUrl);
    if (response.status !== 200) {
      throw new Error(`下载失败,状态码:${response.status}`);
    }

    const urls = [];
    // 解析 XML
    parseString(response.data, (err, result) => {
      if (err) {
        console.error('解析失败:', err);
        return;
      }
      const urlElements = result.urlset?.url;
      if (urlElements) {
        urls.push(...urlElements.map(urlObj => urlObj.loc[0]));
      }
    });

    if (urls.length === 0) {
      console.warn('未找到有效 URL');
      return;
    }

    // 构建请求体
    const hostname = new URL(sitemapUrl).hostname;
    const body = JSON.stringify({
      host: hostname,
      key: apiKey,
      keyLocation: `https://${hostname}/${apiKey}.txt`,
      urlList: urls
    });

    // 发送到 Bing API
    const postResponse = await axios.post(
      'https://api.indexnow.org/IndexNow',
      body,
      { headers: { 'Content-Type': 'application/json' } }
    );

    if (postResponse.status === 200) {
      console.log('提交成功:', postResponse.data);
    } else {
      throw new Error(`Bing API 返回错误,状态码:${postResponse.status}`);
    }
  } catch (error) {
    console.error('错误:', error.message);
  }
}

module.exports = { submitSitemapToBing };

课后作业

尝试添加以下功能: – 日志增强:记录提交的 URL 数量和耗时 – 漂亮的 CLI:为工具制作一个漂亮的 CLI 界面 – 批量处理:支持同时提交多个 sitemap(如 ['sitemap1.xml', 'sitemap2.xml']) – 错误重试:遇到网络问题时自动重试 3 次

深入理解 IndexNow

阅读 IndexNow 官方文档

结语

通过这个工具,你可以让 Bing 更高效地收录你的网页,但记住:
自动化工具只是手段,内容质量才是 SEO 的核心!

 
阅读更多

from WolfYangFan / 小狼阳帆

本文通过解读一个生产级 WHOIS 查询服务的 Golang 语言实现,探讨 API 设计中的关键要素:类型验证、速率控制、异步处理和优雅降级。该实现符合 RFC 标准,支持 ASN/IPv4/IPv6/域名 查询,适用于网络监控和网络安全场景。


架构概览

服务采用经典分层设计: 1. 配置层:环境变量注入 2. 控制层:请求验证与限流 3. 业务层:WHOIS查询处理 4. 展示层:标准化JSON响应

核心实现解析

1. 环境感知配置加载

func loadConfig() *Config {
    port := 8080
    if p := os.Getenv("PORT"); p != "" {
        fmt.Sscanf(p, "%d", &port)
    }
    // 其他配置项...
}

技术要点: 1. 环境变量优先级高于默认值 2. 类型安全转换保障配置可靠性

2. 流量控制实现

limiter := rate.NewLimiter(cfg.RateLimit, cfg.Burst)

设计考量: 1. 基于令牌桶算法实现平滑限流 2. 突发容量(Burst)设置为限速值的2倍,平衡突发流量与系统保护 3. 中间件模式实现非侵入式集成

3. 输入验证机制

var (
    asnRegex  = regexp.MustCompile(...)  // RFC 3770
    ipv4Regex = regexp.MustCompile(...)  // RFC 791
    ipv6Regex = regexp.MustCompile(...)  // RFC 5952
)

标准化实践: 1. 预处理输入:strings.ToLower(strings.TrimSpace(raw)) 2. 分级验证策略:正则匹配 → net.ParseIP 兜底 3. 错误响应包含预期格式示例

4. 异步查询处理

ctx, cancel := context.WithTimeout(r.Context(), cfg.Timeout)
defer cancel()

go func() {
    // WHOIS查询逻辑
}()

并发模型: 1. 上下文超时控制(10s默认) 2. 使用通道实现 Goroutine 生命周期管理 3. 双通道 (errCh/resultCh) 错误隔离

5. 响应标准化

{
    "code": 200,
    "msg": "success",
    "data": "WHOIS 结果",
    "meta": {
        "query_type": "ipv4",
        "query": "8.8.8.8",
        "timestamp": 1720183143,
        "version": "2025.3"
    }
}

设计规范: 1. 元数据包含语义化版本 2. 错误响应分级: * 4xx:客户端输入问题 * 5xx:服务端处理异常

部署建议

# 自定义部署参数示例
PORT=8443 \
TIMEOUT=15 \
RATE_LIMIT=120 \
./whois-service

课后练习

场景扩展:现需新增缓存查询功能

实现要求: 1. 设计扩展的缓存返回逻辑 2. 维护现有 API 的响应格式标准,比如加入是否为缓存的字段 3. 实现 Redis 缓存层 4. 实现定期/手动刷新缓存逻辑

设计思考: 1. 如何避免引入第三方服务的单点故障? 2. 缓存策略如何平衡实时性和性能?

完整代码

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/likexian/whois"
	"golang.org/x/time/rate"
)

var (
	// 正则验证
	asnRegex  = regexp.MustCompile(`^(?i)^asn?[\d_]{1,15}$`)
	ipv4Regex = regexp.MustCompile(`^(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}$`)
	ipv6Regex = regexp.MustCompile(`^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$`)
)

type Config struct {
	Port      int           `json:"port"`
	Timeout   time.Duration `json:"timeout"`
	RateLimit rate.Limit    `json:"rate_limit"`
	Burst     int           `json:"burst"`
}

type Response struct {
	Code    int         `json:"code"`
	Message string      `json:"msg"`
	Data    interface{} `json:"data,omitempty"`
	Meta    Metadata    `json:"meta"`
}

type Metadata struct {
	QueryType  string `json:"query_type"`
	Query      string `json:"query"`
	Timestamp  int64  `json:"timestamp"`
	APIVersion string `json:"version"`
}

func loadConfig() *Config {
	port := 8080
	if p := os.Getenv("PORT"); p != "" {
		fmt.Sscanf(p, "%d", &port)
	}

	timeout := 10
	if t := os.Getenv("TIMEOUT"); t != "" {
		fmt.Sscanf(t, "%d", &timeout)
	}

	rl := rate.Limit(60)
	if r := os.Getenv("RATE_LIMIT"); r != "" {
		var limit float64
		fmt.Sscanf(r, "%f", &limit)
		rl = rate.Limit(limit)
	}

	return &Config{
		Port:      port,
		Timeout:   time.Duration(timeout) * time.Second,
		RateLimit: rl,
		Burst:     int(rl * 2),
	}
}

func WhoisHandler(limiter *rate.Limiter, cfg *Config) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")

		// 方法过滤
		if r.Method != http.MethodGet {
			sendError(w, http.StatusMethodNotAllowed, "method not allowed", nil)
			return
		}

		// 速率限制
		if !limiter.Allow() {
			sendError(w, http.StatusTooManyRequests, "too many requests", nil)
			return
		}

		// 参数提取
		query := strings.TrimSpace(r.URL.Query().Get("query"))
		if query == "" {
			sendError(w, http.StatusBadRequest, "missing `query` parameter", nil)
			return
		}

		// 查询验证
		qType := DetectQueryType(query)
		if qType == "unknown" {
			sendError(w, http.StatusBadRequest, "invalid query format", map[string]string{
				"allowed":  "asn, ipv4, ipv6, domain",
				"received": query,
			})
			return
		}

		// 异步查询
		ctx, cancel := context.WithTimeout(r.Context(), cfg.Timeout)
		defer cancel()

		resultCh := make(chan string)
		errCh := make(chan error)

		go func() {
			result, err := whois.NewClient().Whois(query)
			if err != nil {
				errCh <- err
				return
			}
			resultCh <- strings.TrimSpace(result)
		}()

		select {
		case result := <-resultCh:
			json.NewEncoder(w).Encode(Response{
				Code:    http.StatusOK,
				Message: "success",
				Data:    result,
				Meta: Metadata{
					QueryType:  qType,
					Query:      query,
					Timestamp:  time.Now().Unix(),
					APIVersion: "2025.3",
				},
			})
		case <-ctx.Done():
			sendError(w, http.StatusRequestTimeout, "query timeout", map[string]interface{}{
				"timeout": cfg.Timeout.Seconds(),
			})
		case err := <-errCh:
			sendError(w, http.StatusServiceUnavailable, "whois query failed", map[string]string{
				"details": err.Error(),
			})
		}
	}
}

func sendError(w http.ResponseWriter, code int, msg string, details interface{}) {
	w.WriteHeader(code)
	json.NewEncoder(w).Encode(Response{
		Code:    code,
		Message: msg,
		Data:    details,
		Meta: Metadata{
			Timestamp:  time.Now().Unix(),
			APIVersion: "2025.3",
		},
	})
}

func DetectQueryType(raw string) string {
	query := strings.ToLower(strings.TrimSpace(raw))

	switch {
	case asnRegex.MatchString(query):
		return "asn"
	case ipv4Regex.MatchString(query):
		return "ipv4"
	case ipv6Regex.MatchString(query):
		return "ipv6"
	case net.ParseIP(query) != nil:
		return "ip"
	case strings.Contains(query, "."):
		return "domain"
	default:
		return "unknown"
	}
}

func main() {
	cfg := loadConfig()
	limiter := rate.NewLimiter(cfg.RateLimit, cfg.Burst)

	mux := http.NewServeMux()
	mux.HandleFunc("/whois", WhoisHandler(limiter, cfg))

	server := &http.Server{
		Addr:         fmt.Sprintf(":%d", cfg.Port),
		Handler:      mux,
		ReadTimeout:  cfg.Timeout,
		WriteTimeout: cfg.Timeout,
	}

	fmt.Printf("WHOIS API v2025.3 running on :%d\n", cfg.Port)
	if err := server.ListenAndServe(); err != nil {
		panic(fmt.Sprintf("Server failed: %v", err))
	}
}

感谢你阅读到这里,如果想要提交课后作业,请提交至 yangfan#5.1.e.7.0.a.a.e.0.a.2.ip6.arpa,欢迎您的投稿!

 
Read more...

from WolfYangFan / 小狼阳帆

你好,世界!


此刻是乙巳蛇年二月十一的黄昏,窗外的暮色正将城市折叠成一行行未完成的代码。当我敲下「Hello, World!」时,屏幕的蓝光在暗室中裂开一道缝隙——这不仅是程序员的入门仪式,更是人类对存在本质的终极叩问。
在古希腊,泰勒斯说「万物源于水」;在东方,《易经》以「太极生两仪」诠释世界的分化。而今天,「Hello, World!」以二进制逻辑重构了创世神话:一段代码的诞生,暗喻着生命从虚无中涌现的奇迹。它提醒我们:每一个伟大的存在,最初都只是黑暗中的一粒萤火。

每一个程序员都曾因漏写分号而崩溃,而生命的bug往往更具颠覆性:
* 语法错误(Syntax Error)
当理想与现实冲突时,我们像一段被编译器拒绝的代码。但但丁的《神曲》恰始于迷失森林,佛陀的觉悟源于生老病死之痛——所有「错误」都是重构认知的契机。
* 逻辑错误(Logical Error)
那些能运行却偏离预期的代码,恰似当代人的生存困境:按部就班地生活,却感到意义悄然溃散。此时需要的不只是调试工具,更是苏格拉底式的「精神助产术」。
* 内存泄漏(Memory Leak)
当过去的创伤占据太多心理资源,生命将陷入卡顿。古老的东方智慧早已给出解法:「应无所住而生其心」(《金刚经》),如同垃圾回收机制释放冗余数据。

让我们重新凝视屏幕上闪烁的「Hello, World!」—— 它不仅是向机器发出的问候,更是对存在的确认、对可能的召唤。每一个生命都是一段独特的代码:有人活成优雅的 Python,有人成为严谨的 C++,有人则在 Lisp 的括号森林中寻找自由。

在这个算法试图定义一切的时代,愿「狼言狼语」成为一片保留野性的精神原野。让我们像狼群呼唤月亮那样,用代码写诗,用文字编程,在数字与灵性的交织中,重构属于这个时代的「创世记」。

Hello, World!

Hello, Uncharted Wilderness!

 
阅读更多