sqlite-utils: วิธีที่ดีในการนำเข้าข้อมูลไปยัง SQLite เพื่อการวิเคราะห์

สวัสดี! นี่เป็นโพสต์สั้นๆ เกี่ยวกับเครื่องมือดีๆ ที่ฉันเพิ่งพบว่าเรียกว่า sqlite-utils จาก หมวดเครื่องมือ

เมื่อเร็ว ๆ นี้ฉันต้องการวิเคราะห์ข้อมูลพื้นฐานโดยใช้ข้อมูลจากร้านค้า Shopify ของฉัน ดังนั้นฉันจึงคิดว่าฉันจะสอบถาม Shopify API และนำเข้าข้อมูลของฉันไปยัง SQLite จากนั้นฉันก็สามารถสืบค้นเพื่อให้ได้กราฟที่ต้องการ

แต่นี่ดูเหมือนเป็นงานที่น่าเบื่อมาก เหมือนกับว่าฉันต้องเขียนสคีมาและเขียนโปรแกรม Python ดังนั้นฉันจึงค้นหาวิธีแก้ปัญหา และพบ sqlite-utils ซึ่งเป็นเครื่องมือที่ออกแบบมาเพื่อให้ง่ายต่อการนำเข้าข้อมูลที่กำหนดเองไปยัง SQLite เพื่อทำการวิเคราะห์ข้อมูล

sqlite-utils สร้าง schema . โดยอัตโนมัติ

ข้อมูล Shopify มีฟิลด์ประมาณหนึ่งพันล้านฟิลด์ และฉันไม่ต้องการพิมพ์สคีมาสำหรับข้อมูลนั้นจริงๆ sqlite-utils แก้ปัญหานี้ได้: ถ้าฉันมีอาร์เรย์ของคำสั่ง JSON ฉันสามารถสร้างตาราง SQLite ใหม่ด้วยข้อมูลดังกล่าวได้ดังนี้:

 import sqlite_utils orders = ... # (some code to get the `orders` array here) db = sqlite_utils.Database('orders.db') db['shopify_orders'].insert_all(orders)

คุณสามารถเปลี่ยนสคีมาได้หากมีฟิลด์ใหม่ (พร้อม alter )

ต่อไป ฉันพบปัญหาที่ในหน้าดาวน์โหลดที่ 5 JSON มีฟิลด์ใหม่ที่ฉันไม่เคยเห็นมาก่อน

โชคดีที่ sqlite-utils คิดว่า: มีแฟล็ก alter ซึ่งจะอัปเดตสคีมาของตารางเพื่อรวมฟิลด์ใหม่ “`

นี่คือรหัสสำหรับที่ดูเหมือน

 db['shopify_orders'].insert_all(orders, alter=True)

คุณสามารถขจัดแถวที่มีอยู่ออก (ด้วย upsert )

ต่อไป ฉันพบปัญหาที่บางครั้งเมื่อทำการซิงค์ ฉันจะดาวน์โหลดข้อมูลจาก API ซึ่งบางส่วนเป็นข้อมูลใหม่และบางส่วนไม่ใช่

ดังนั้นฉันจึงต้องการทำ “upsert” ซึ่งจะสร้างแถวใหม่เฉพาะในกรณีที่ยังไม่มีรายการนั้น sqlite-utils ก็คิดเรื่องนี้เช่นกันและมีวิธี upsert

เพื่อให้ใช้งานได้ คุณต้องระบุคีย์หลัก สำหรับฉันนั่นคือ pk="id" นี่คือลักษณะรหัสสุดท้ายของฉัน:

 db['shopify_orders'].upsert_all( orders, pk="id", alter=True )

นอกจากนี้ยังมีเครื่องมือบรรทัดคำสั่ง

ฉันได้พูดคุยเกี่ยวกับการใช้ sqlite-utils เป็นห้องสมุดแล้ว แต่ก็ยังมีเครื่องมือบรรทัดคำสั่งที่มีประโยชน์จริงๆ

ตัวอย่างเช่น การแทรกข้อมูลจาก plants.csv ลงในตาราง plants :

 sqlite-utils insert plants.db plants plants.csv --csv

การแปลงรูปแบบ

ฉันยังไม่ได้ลองสิ่งนี้ แต่นี่เป็นตัวอย่างที่ยอดเยี่ยมจากเอกสารช่วยเหลือเกี่ยวกับวิธีการแปลงรูปแบบ เช่น การแปลงสตริงเป็นทศนิยม:

 sqlite-utils insert plants.db plants plants.csv --csv --convert ' return { "name": row["name"].upper(), "latitude": float(row["latitude"]), "longitude": float(row["longitude"]), }'

ดูเหมือนว่าจะมีประโยชน์จริง ๆ สำหรับ CSV โดยค่าเริ่มต้นมักจะตีความข้อมูลตัวเลขเป็นสตริงหากคุณไม่ทำการแปลงนี้

metabase ก็ดูดีเหมือนกัน

เมื่อฉันมีข้อมูลทั้งหมดใน SQLite แล้ว ฉันต้องการวิธีวาดกราฟด้วย ฉันต้องการแดชบอร์ด ดังนั้นฉันจึงลงเอยด้วยการใช้ Metabase ซึ่งเป็นเครื่องมือข่าวกรองธุรกิจแบบโอเพ่นซอร์ส ฉันพบว่ามันตรงไปตรงมามาก และดูเหมือนว่าจะเป็นวิธีง่ายๆ ในการเปลี่ยนการสืบค้น SQL ให้เป็นกราฟ

การตั้งค่าทั้งหมดนี้ (sqlite-utils + metabase + SQL) ให้ความรู้สึกว่าใช้งานได้ง่ายกว่าการตั้งค่าก่อนหน้านี้มาก โดยที่ฉันมีเว็บไซต์ Flask แบบกำหนดเองที่ใช้พล็อตเรื่องและแพนด้าในการวาดกราฟ

นั่นคือทั้งหมด!

ฉันพอใจมากกับ sqlite-utils มันใช้งานง่ายสุด ๆ และทำทุกอย่างที่ฉันต้องการ

หน้าที่ไม่ได้ทำให้เป็น “วิธีการทำงานของ DNS”

สวัสดี! สองสามสัปดาห์ก่อน ฉันเปิดตัวนิตยสารใหม่ชื่อ How DNS Works

เมื่อฉันเริ่มเขียนนิตยสารเล่มนั้น (ในเดือนมกราคม พ.ศ. 2564) ตอนแรกฉันนึกถึงนิตยสารที่กว้างขึ้นเกี่ยวกับ “ทุกสิ่งที่คุณจำเป็นต้องรู้เพื่อเป็นเจ้าของโดเมน” จึงมีหน้ามากมายเกี่ยวกับการจดทะเบียนโดเมน TLS และอีเมล

ตอนนั้นฉันคิดว่า “ฉันสามารถอธิบาย DNS ได้ใน 5 หน้า มันไม่ได้ซับซ้อนขนาดนั้น และจะมีพื้นที่มากมายสำหรับหัวข้ออื่นๆ เกี่ยวกับโดเมน” ฉันคิดผิดอย่างมหันต์ และกลายเป็นว่าฉันต้องการทั้ง 28 หน้าเพื่ออธิบาย DNS ดังนั้นฉันจึงตัดสินใจเน้นที่ซีนบน DNS และหัวข้ออื่นๆ ทั้งหมดก็ไม่ได้ทำให้เป็นซีนสุดท้าย

เมื่อเช้านี้ ข้าพเจ้ารู้สึกว่า แทนที่จะปล่อยให้หน้าฉบับร่างเก่า ๆ หมดไปในนรกบนฮาร์ดไดรฟ์ ฉันสามารถโพสต์หน้าพิเศษเหล่านั้นที่นี่พร้อมกันในบล็อกของฉันได้ ดังนั้นพวกเขาจึงอยู่ที่นี่!

ข้อจำกัดความรับผิดชอบ: ไม่เหนียวแน่น

ฉันจะพูด (ในฐานะข้อจำกัดความรับผิดชอบ) ว่าหน้าเหล่านี้ไม่เหนียวแน่นเหมือนที่ฉันชอบให้ซีนของฉันเป็น และพวกเขาไม่ได้บอกคุณทุกสิ่งที่คุณจำเป็นต้องรู้เพื่อเป็นเจ้าของโดเมนอย่างแน่นอน

การลงทะเบียนโดเมน

buy-domain.pngregistrar.pngdomain-expiry.pngโอน.pngdomain-privacy.pngtld.png

อีเมล

รับ-email.pngส่ง-email.pngspf-dkim.png

ฉันควรบอกว่า 3 หน้านี้ไม่ยุติธรรมกับอีเมลจริงๆ – ความปลอดภัยของอีเมลเป็นหัวข้อใหญ่ที่ฉันไม่รู้จริงๆ

TLS

ทำไม-tls-matters.pngtls-certificates.png

สองหน้านี้ไม่ได้ครอบคลุม TLS จากระยะไกล เป็นไปได้ว่าฉันจะเขียนเชิงลึกเพิ่มเติมเกี่ยวกับ TLS ในบางประเด็น ใครจะรู้!

นั่นคือทั้งหมด!

แม้ว่าฉันจะพูดว่า: ถ้าคุณชอบสิ่งเหล่านี้ คุณอาจสนใจซื้อ How DNS Works 🙂

รายการเครื่องมือบรรทัดคำสั่งใหม่ (ish)

สวัสดี! วันนี้ฉันถาม บน Twitter เกี่ยวกับเครื่องมือบรรทัดคำสั่งที่ใหม่กว่า เช่น ripgrep และ fd และ fzf และ exa และ bat

ฉันได้รับคำตอบมากมายเกี่ยวกับเครื่องมือที่ฉันไม่เคยรู้จักมาก่อน เลยคิดว่าจะเขียนรายการไว้ที่นี่ หลายคนชี้ไปที่รายการ ยูนิกซ์สมัยใหม่

ทดแทนเครื่องมือมาตรฐาน

สิ่งประดิษฐ์ใหม่

ต่อไปนี้คือเครื่องมือบางอย่างที่ไม่สามารถทดแทนเครื่องมือมาตรฐานได้อย่างแน่นอน:

  • z , fasd , autojump , zoxide (เครื่องมือที่ช่วยให้ค้นหาไฟล์ / เปลี่ยนไดเร็กทอรีได้ง่ายขึ้น)
  • broot (ตัวจัดการไฟล์)
  • direnv (โหลดตัวแปรสภาพแวดล้อมขึ้นอยู่กับไดเร็กทอรีปัจจุบัน)
  • fzf , peco (“ ตัวค้นหาคลุมเครือ”)
  • croc และ magic-wormhole (ส่งไฟล์จากคอมพิวเตอร์เครื่องหนึ่งไปยังอีกเครื่องหนึ่ง)
  • ไฮเปอร์ไฟน์ (การเปรียบเทียบ)
  • httpie , curlie , xh (สำหรับส่งคำขอ HTTP)
  • entr (เรียกใช้คำสั่งโดยพลการเมื่อไฟล์เปลี่ยน)
  • asdf (ตัวจัดการเวอร์ชันสำหรับหลายภาษา)
  • tig , lazygit (อินเทอร์เฟซแบบโต้ตอบสำหรับ git)
  • lazydocker (อินเทอร์เฟซแบบโต้ตอบสำหรับนักเทียบท่า)
  • เลือก (พื้นฐานของ awk/cut)
  • ctop (ท็อปสำหรับคอนเทนเนอร์)
  • เพศสัมพันธ์ (ข้อผิดพลาดบรรทัดคำสั่งแก้ไขอัตโนมัติ)
  • pbcopy/pbpaste (สำหรับคลิปบอร์ด <> stdin/stdout) อาจไม่ใช่ “ใหม่” แต่มีผู้กล่าวถึงเป็นจำนวนมาก คุณสามารถ ใช้ xclip เพื่อทำสิ่งเดียวกันบน Linux

สิ่ง JSON/YAML/CSV:

  • jq (เครื่องมือสร้างปัญหา JSON ที่ยอดเยี่ยม)
  • jc (แปลงเอาต์พุตของเครื่องมือต่างๆ เป็น JSON)
  • yq (เช่น jq แต่สำหรับ YAML) ยังมี อีก yq
  • fq (เช่น jq แต่สำหรับไบนารี)
  • fx (เครื่องมือ json แบบโต้ตอบ)
  • jless (json เพจเจอร์)
  • xsv (เครื่องมือบรรทัดคำสั่งสำหรับไฟล์ csv จาก burntsushi)
  • visitdata (“ multitool แบบโต้ตอบสำหรับข้อมูลแบบตาราง”)

สิ่งที่ grep:

  • pdfgrep (grep สำหรับ PDF)
  • gron (ทำให้ JSON Greppable)
  • ripgrep-all (ripgrep แต่ยังรวมถึง PDF, zip, ebooks เป็นต้น)

รายการโปรดของฉัน

รายการโปรดของฉันที่ฉันใช้อยู่แล้วคือ entr , ripgrep , git-delta , httpie , plocate และ jq ฉันสนใจที่จะลองใช้ btm , z , xsv และ duf

การนำ TLS 1.3 . เวอร์ชันของเล่นไปใช้

สวัสดี! เมื่อเร็ว ๆ นี้ ฉันกำลังคิดว่าจะสนุกกับการเรียนระบบเครือข่ายคอมพิวเตอร์โดยใช้โปรโตคอลเครือข่ายจริงในเวอร์ชันที่ใช้งานได้จริงได้อย่างไร

และมันทำให้ฉันสงสัย – ฉันใช้เวอร์ชันของเล่นของ traceroute , TCP และ DNS แล้ว TLS ล่ะ? ฉันสามารถใช้เวอร์ชันของเล่นเพื่อเรียนรู้เพิ่มเติมเกี่ยวกับวิธีการทำงานได้หรือไม่

ฉันถามใน Twitter ว่ามันจะยากไหม ได้รับ กำลังใจและคำแนะนำว่าควรเริ่มจากตรงไหน ฉันจึงตัดสินใจทำ

มันสนุกจริงๆ และฉันได้เรียนรู้เพิ่มเติมเล็กน้อยเกี่ยวกับความเกี่ยวข้องของการเข้ารหัสที่แท้จริง – ฉันเชื่อ 100% แล้วว่าฉันไม่ควรประดิษฐ์การใช้งาน crypto ของตัวเอง และการเห็นว่า crypto ใน TLS 1.3 ทำงานอย่างไร ก็ยิ่งรู้สึกซาบซึ้งมากขึ้นว่าทำไม ฉันไม่ควร 🙂

เพื่อเป็นการเตือน: ฉันไม่ใช่คนเข้ารหัสจริงๆ ฉันอาจจะพูดสิ่งที่ไม่ถูกต้องเกี่ยวกับการเข้ารหัสในโพสต์นี้ และฉันไม่ทราบประวัติของช่องโหว่ TLS ในอดีตที่แจ้งการออกแบบของ TLS 1.3 เลย

จากทั้งหมดที่กล่าวมา มาเริ่มใช้งานการเข้ารหัสกันเถอะ! รหัสแฮ็กทั้งหมดของฉันอยู่ ใน github

ความเรียบง่าย

ฉันเพียงต้องการทำงานเกี่ยวกับเรื่องนี้เพียงไม่กี่วันเท่านั้น ดังนั้นฉันจึงจำเป็นต้องทำให้เข้าใจง่ายขึ้นอย่างมากเพื่อให้เสร็จได้อย่างรวดเร็ว

ฉันตัดสินใจว่าเป้าหมายของฉันคือดาวน์โหลดหน้าแรกของบล็อกนี้ด้วย TLS ดังนั้นฉันจึงไม่ต้องปรับใช้ TLS ทั่วไปโดยสมบูรณ์ ฉันแค่ต้องเชื่อมต่อกับเว็บไซต์เดียวให้สำเร็จ

โดยเฉพาะอย่างยิ่ง นี่หมายความว่า:

  • ฉันสนับสนุนชุดรหัสเดียวเท่านั้น
  • ฉันไม่ตรวจสอบใบรับรองของเซิร์ฟเวอร์เลย ฉันแค่เพิกเฉย
  • การแยกวิเคราะห์และการจัดรูปแบบข้อความของฉันอาจดูหยาบและเปราะบางมาก เพราะฉันต้องสามารถพูดคุยกับการใช้งาน TLS ที่เฉพาะเจาะจงได้เพียงอย่างเดียว (และเชื่อฉันเถอะ พวกเขาเป็นเช่นนั้น)

ทรัพยากร TLS ที่น่าทึ่ง: tls13.ulfheim.net

โชคดีที่ก่อนเริ่มสิ่งนี้ ฉันจำได้ไม่ชัดว่าเคยเห็นเว็บไซต์ที่อธิบายทุกๆ ไบต์ในการเชื่อมต่อ TLS 1.3 พร้อมตัวอย่างโค้ดโดยละเอียดเพื่อทำซ้ำทุกส่วน googling บางคนเปิดเผยว่ามันคือ The New Illustrated TLS Connection

ฉันไม่สามารถเน้นว่าสิ่งนี้มีประโยชน์เพียงใด ฉันดูมากกว่าร้อยครั้งแล้วดู TLS 1.3 RFC สำหรับสิ่งเล็ก ๆ น้อย ๆ เท่านั้น

พื้นฐานการเข้ารหัสบางอย่าง

ก่อนที่ฉันจะเริ่มทำงานนี้ ความเข้าใจของฉันเกี่ยวกับ TLS คือ:

  1. ที่จุดเริ่มต้นมีการแลกเปลี่ยนคีย์ Diffie-Hellman บางประเภท
  2. คุณใช้การแลกเปลี่ยนคีย์เพื่อรับคีย์สมมาตร AES (อย่างไร) และเข้ารหัสการเชื่อมต่อที่เหลือด้วย AES

ถูกต้อง แต่ปรากฏว่ามันซับซ้อนกว่านั้น

โอเค มาเริ่มใช้งาน TLS ของเล่นแฮ็คของฉันกัน หวังว่าจะดำเนินไปโดยไม่ได้บอกว่าคุณไม่ควรใช้รหัสนี้เพื่อสิ่งใดโดยเด็ดขาด

ขั้นตอนที่ 1: กล่าวสวัสดี

อันดับแรก เราต้องส่งข้อความ “สวัสดีลูกค้า” สำหรับจุดประสงค์ของฉัน มีข้อมูลเพียง 4 ชิ้นในนั้น:

  1. คีย์สาธารณะที่สร้างแบบสุ่ม
  2. ข้อมูลสุ่ม 32 ไบต์ (“สุ่มไคลเอ็นต์”)
  3. ชื่อโดเมนที่ฉันต้องการเชื่อมต่อ ( jvns.ca )
  4. ชุดรหัส/อัลกอริธึมลายเซ็นที่เราต้องการใช้ (ซึ่งฉันเพิ่งคัดลอกมาจาก tls.ulfheim.net) กระบวนการเจรจานี้ค่อนข้างสำคัญโดยทั่วไป แต่ฉันไม่สนใจเพราะฉันสนับสนุนอัลกอริธึมลายเซ็น / ชุดการเข้ารหัสเดียวเท่านั้น

ส่วนที่น่าสนใจที่สุดสำหรับฉันคือส่วนที่ 1 – ฉันจะสร้างกุญแจสาธารณะได้อย่างไร

ฉันสับสนเกี่ยวกับสิ่งนี้อยู่พักหนึ่ง แต่มันจบลงด้วยโค้ดเพียง 2 บรรทัด

 privateKey := random(32) publicKey, err := curve25519.X25519(privateKey, curve25519.Basepoint)

คุณสามารถดูโค้ดที่เหลือเพื่อสร้าง ข้อความสวัสดีลูกค้าได้ที่นี่ แต่มันน่าเบื่อมาก มันแค่เล่นซอกันนิดหน่อย

การเข้ารหัสเส้นโค้งวงรีนั้นยอดเยี่ยม

ฉันจะไม่อธิบายเกี่ยวกับการเข้ารหัสเส้นโค้งวงรีที่นี่ แต่ฉันแค่อยากจะบอกว่ามันเจ๋งแค่ไหนที่คุณสามารถ:

  • สร้างสตริง 32 ไบต์แบบสุ่มเป็นคีย์ส่วนตัว
  • “คูณ” คีย์ส่วนตัวด้วยจุดฐานของเส้นโค้งเพื่อรับกุญแจสาธารณะ (นี่คือ “การคูณ” ของเส้นโค้งวงรีโดยที่ n * P หมายถึง “เพิ่ม P ให้กับตัวเอง n ครั้ง”)
  • แค่นั้นแหละ!!

ฉันจะไม่พูดเพิ่มเติมเกี่ยวกับการเข้ารหัสเส้นโค้งวงรีที่นี่ แต่ฉันชอบความง่ายในการใช้งาน – ดูเหมือนตรงไปตรงมากว่า RSA มากซึ่งคีย์ส่วนตัวของคุณต้องเป็นตัวเลขเฉพาะ

ฉันไม่รู้ว่า “คุณสามารถสร้างสตริงขนาด 32 ไบต์ใดๆ เป็นไพรเวตคีย์” ได้หรือไม่สำหรับเส้นโค้งวงรีทั้งหมดหรือสำหรับเส้นโค้งวงรีเฉพาะนี้ ( Curve25519 )

ขั้นตอนที่ 2: แยกวิเคราะห์เซิร์ฟเวอร์ hello

ถัดไปเซิร์ฟเวอร์กล่าวสวัสดี มันน่าเบื่อมาก โดยพื้นฐานแล้วเราแค่ต้องแยกวิเคราะห์เพื่อรับกุญแจสาธารณะของเซิร์ฟเวอร์ซึ่งมีขนาด 32 ไบต์ นี่คือรหัสแม้ว่า

ขั้นตอนที่ 3: คำนวณคีย์เพื่อเข้ารหัสการจับมือกัน

ตอนนี้เรามีคีย์สาธารณะของเซิร์ฟเวอร์แล้ว และเราได้ส่งคีย์สาธารณะของเราไปยังเซิร์ฟเวอร์แล้ว เราสามารถเริ่มคำนวณคีย์ที่เราจะใช้ในการเข้ารหัสข้อมูลได้จริง

ฉันรู้สึกประหลาดใจที่รู้ว่ามีคีย์สมมาตรอย่างน้อย 4 คีย์ที่เกี่ยวข้องใน TLS:

  • คีย์การจับมือลูกค้า/iv (สำหรับข้อมูลที่ลูกค้าส่งในการจับมือกัน)
  • คีย์การจับมือเซิร์ฟเวอร์/iv (สำหรับข้อมูลที่เซิร์ฟเวอร์ส่งในการจับมือกัน)
  • รหัสแอปพลิเคชันไคลเอ็นต์/iv (สำหรับข้อมูลที่เหลือที่ไคลเอ็นต์ส่ง)
  • รหัสแอปพลิเคชันเซิร์ฟเวอร์/iv (สำหรับข้อมูลที่เหลือที่เซิร์ฟเวอร์ส่ง)
  • ฉันคิดว่าเป็นอีกกุญแจสำคัญสำหรับการเริ่มเซสชันใหม่ แต่ฉันไม่ได้ใช้สิ่งนั้น

เราเริ่มต้นด้วยการรวมคีย์ส่วนตัวของเซิร์ฟเวอร์และคีย์สาธารณะของเราเข้าด้วยกันเพื่อรับความลับที่ใช้ร่วมกัน สิ่งนี้เรียกว่า “elliptic curve diffie hellman” หรือ ECDH และค่อนข้างง่าย: “คูณ” คีย์ส่วนตัวของเซิร์ฟเวอร์ด้วยคีย์สาธารณะของเรา:

 sharedSecret, err := curve25519.X25519(session.Keys.Private, session.ServerHello.PublicKey)

ซึ่งจะทำให้เรามีคีย์ลับขนาด 32 ไบต์ที่ทั้งไคลเอ็นต์และเซิร์ฟเวอร์มี เย้!

แต่เราต้องการ 96 ไบต์ (16 + 12) * 4 คีย์ทั้งหมด นั่นมากกว่า 32 ไบต์!

เวลาสำหรับการได้มาที่สำคัญ

เห็นได้ชัดว่าวิธีที่คุณเปลี่ยนคีย์ขนาดเล็กเป็นคีย์เพิ่มเติมเรียกว่า “การสร้างคีย์” และ TLS 1.3 ใช้อัลกอริธึมที่เรียกว่า “HKDF” ในการดำเนินการนี้ ฉันไม่เข้าใจสิ่งนี้จริง ๆ แต่นี่คือสิ่งที่รหัสของฉันต้องทำ

ดูเหมือนว่าจะเกี่ยวข้องกับการเรียก hkdf.Expand และ hkdf.Extract กันหลายครั้ง

 func (session *Session) MakeHandshakeKeys() { zeros := make([]byte, 32) psk := make([]byte, 32) // ok so far if err != nil { panic(err) } earlySecret := hkdf.Extract(sha256.New, psk, zeros) // TODO: psk might be wrong derivedSecret := deriveSecret(earlySecret, "derived", []byte{}) session.Keys.HandshakeSecret = hkdf.Extract(sha256.New, sharedSecret, derivedSecret) handshakeMessages := concatenate(session.Messages.ClientHello.Contents(), session.Messages.ServerHello.Contents()) cHsSecret := deriveSecret(session.Keys.HandshakeSecret, "c hs traffic", handshakeMessages) session.Keys.ClientHandshakeSecret = cHsSecret session.Keys.ClientHandshakeKey = hkdfExpandLabel(cHsSecret, "key", []byte{}, 16) session.Keys.ClientHandshakeIV = hkdfExpandLabel(cHsSecret, "iv", []byte{}, 12) sHsSecret := deriveSecret(session.Keys.HandshakeSecret, "s hs traffic", handshakeMessages) session.Keys.ServerHandshakeKey = hkdfExpandLabel(sHsSecret, "key", []byte{}, 16) session.Keys.ServerHandshakeIV = hkdfExpandLabel(sHsSecret, "iv", []byte{}, 12) }

การทำงานนี้ค่อนข้างน่ารำคาญเพราะฉันเอาแต่โต้แย้งเรื่องต่างๆ ที่ไม่ถูกต้อง เหตุผลเดียวที่ฉันจัดการได้ก็เพราะ https://tls13.ulfheim.net มีอินพุตและเอาต์พุตตัวอย่างจำนวนมากและโค้ดตัวอย่าง ดังนั้นฉันจึงสามารถเขียนการทดสอบหน่วยและตรวจสอบโค้ดของฉันกับตัวอย่างการใช้งานไซต์

อย่างไรก็ตาม ในที่สุดฉันก็ได้คีย์ทั้งหมดของฉันแล้ว และถึงเวลาเริ่มถอดรหัสแล้ว!

นอกเหนือจาก IVs

สำหรับแต่ละคีย์จะมี “IV” ซึ่งย่อมาจาก “initialization vector” แนวคิดน่าจะเป็นการใช้เวกเตอร์การเริ่มต้นที่แตกต่างกันสำหรับทุกข้อความที่เราเข้ารหัส/ถอดรหัส สำหรับ More Security ™

ในการใช้งานนี้ วิธีที่เราได้รับ IV ที่แตกต่างกันสำหรับแต่ละข้อความคือการ xoring IV ด้วยจำนวนข้อความที่ส่ง/รับจนถึงตอนนี้

ขั้นตอนที่ 4: เขียนรหัสถอดรหัส

ตอนนี้เรามีคีย์และ IV เหล่านี้แล้ว เราก็สามารถเขียนฟังก์ชัน decrypt ได้

ฉันคิดว่า TLS เพิ่งใช้ AES แต่เห็นได้ชัดว่ามันใช้ “การเข้ารหัสการตรวจสอบสิทธิ์” ที่ด้านบนของ AES ที่ฉันไม่เคยได้ยินมาก่อน

คำอธิบายบทความวิกิพีเดียของการเข้ารหัสที่พิสูจน์ตัวตนนั้นค่อนข้างชัดเจน:

… การเข้ารหัสที่รับรองความถูกต้องสามารถให้การรักษาความปลอดภัยจากการโจมตีแบบเข้ารหัสที่ เลือก ได้ ในการโจมตีเหล่านี้ ฝ่ายตรงข้ามพยายามที่จะได้เปรียบกับระบบการเข้ารหัสลับ (เช่น ข้อมูลเกี่ยวกับคีย์ถอดรหัสลับ) โดยส่ง ciphertexts ที่เลือกอย่างระมัดระวังไปยัง “พยากรณ์การถอดรหัส” และวิเคราะห์ผลลัพธ์ที่ถอดรหัสลับ รูปแบบการเข้ารหัสที่รับรองความถูกต้องสามารถรับรู้ข้อความเข้ารหัสที่สร้างขึ้นอย่างไม่เหมาะสมและปฏิเสธที่จะถอดรหัส ซึ่งจะป้องกันไม่ให้ผู้โจมตีร้องขอการถอดรหัสของข้อความเข้ารหัสใดๆ เว้นแต่จะถูกสร้างขึ้นอย่างถูกต้องโดยใช้อัลกอริธึมการเข้ารหัส

สิ่งนี้สมเหตุสมผลสำหรับฉันเพราะฉันได้ท้าทาย cryptopals บางส่วนและมีการโจมตีแบบนี้ใน cryptopals ชุดที่ 2 (ฉันไม่รู้ว่าเป็นสิ่งเดียวกันหรือเปล่า)

อย่างไรก็ตาม นี่คือโค้ดบางส่วนที่ใช้การเข้ารหัสที่รับรองความถูกต้องตามที่ TLS 1.3 ระบุ ฉันคิดว่า GCM เป็นอัลกอริธึมการเข้ารหัสที่รับรองความถูกต้อง

 func decrypt(key, iv, wrapper []byte) []byte { block, err := aes.NewCipher(key) if err != nil { panic(err.Error()) } aesgcm, err := cipher.NewGCM(block) if err != nil { panic(err.Error()) } additional := wrapper[:5] ciphertext := wrapper[5:] plaintext, err := aesgcm.Open(nil, iv, ciphertext, additional) if err != nil { panic(err.Error()) } return plaintext }

ขั้นตอนที่ 5: ถอดรหัสการจับมือเซิร์ฟเวอร์

ถัดไปเซิร์ฟเวอร์ส่งข้อมูลการจับมือกันเพิ่มเติม ซึ่งมีใบรับรองและสิ่งอื่น ๆ

นี่คือรหัสของฉันสำหรับการถอดรหัสการจับมือ โดยพื้นฐานแล้วมันเพียงอ่านข้อมูลที่เข้ารหัสจากเครือข่าย ถอดรหัส และบันทึก

 record := readRecord(session.Conn) if record.Type() != 0x17 { panic("expected wrapper") } session.Messages.ServerHandshake = decrypt(session.Keys.ServerHandshakeKey, session.Keys.ServerHandshakeIV, record)

คุณอาจสังเกตเห็นว่าเราไม่ได้ แยกวิเคราะห์ ข้อมูลนี้เลย – นั่นเป็นเพราะเราไม่ต้องการเนื้อหา เนื่องจากเราไม่ได้ตรวจสอบใบรับรองของเซิร์ฟเวอร์

เราต้องการมันสำหรับขั้นตอนต่อไป

ขั้นตอนที่ 6: รับคีย์เพิ่มเติม

เราใช้แฮชของข้อมูลแฮนด์เชค SHA256 ที่เราเพิ่งได้รับจากเซิร์ฟเวอร์เพื่อสร้างคีย์ที่สมมาตรยิ่งขึ้น นี่เกือบจะเป็นขั้นตอนสุดท้ายแล้ว!

ซึ่งเกือบจะเหมือนกันทุกประการกับรหัสที่มาของคีย์ก่อนหน้านี้ แต่ฉันรวมไว้เพราะฉันรู้สึกประหลาดใจที่ต้องทำงานมากเพียงใดเพื่อสร้างคีย์เหล่านี้ทั้งหมด

 func (session *Session) MakeApplicationKeys() { handshakeMessages := concatenate( session.Messages.ClientHello.Contents(), session.Messages.ServerHello.Contents(), session.Messages.ServerHandshake.Contents()) zeros := make([]byte, 32) derivedSecret := deriveSecret(session.Keys.HandshakeSecret, "derived", []byte{}) masterSecret := hkdf.Extract(sha256.New, zeros, derivedSecret) cApSecret := deriveSecret(masterSecret, "c ap traffic", handshakeMessages) session.Keys.ClientApplicationKey = hkdfExpandLabel(cApSecret, "key", []byte{}, 16) session.Keys.ClientApplicationIV = hkdfExpandLabel(cApSecret, "iv", []byte{}, 12) sApSecret := deriveSecret(masterSecret, "s ap traffic", handshakeMessages) session.Keys.ServerApplicationKey = hkdfExpandLabel(sApSecret, "key", []byte{}, 16) session.Keys.ServerApplicationIV = hkdfExpandLabel(sApSecret, "iv", []byte{}, 12) }

ขั้นตอนที่ 7: เสร็จสิ้นการจับมือ

ต่อไป เราต้องส่งข้อความ “การจับมือเสร็จสิ้น” ไปยังเซิร์ฟเวอร์เพื่อตรวจสอบว่าทุกอย่างเสร็จสิ้น รหัสนั้นอยู่ ที่นี่

และตอนนี้เราจับมือกันเสร็จแล้ว! นั่นเป็นส่วนที่ยาก การส่งและรับข้อมูลค่อนข้างง่าย

ขั้นตอนที่ 8: ทำการร้องขอ HTTP

ฉันเขียนฟังก์ชัน SendData ที่เข้ารหัสและส่งข้อมูลโดยใช้คีย์ของเรา คราวนี้เราใช้ปุ่ม “application” ไม่ใช่ปุ่ม handshake ทำให้การร้องขอ HTTP ค่อนข้างง่าย:

 req := fmt.Sprintf("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", domain) session.SendData([]byte(req))

ขั้นตอนที่ 9: เราสามารถถอดรหัสการตอบกลับได้จริง !!!

ถึงเวลาที่ฉันรอคอย — จริง ๆ แล้วถอดรหัสการตอบกลับจากเซิร์ฟเวอร์!!! แต่ที่นี่ฉันต้องเรียนรู้อย่างอื่นเกี่ยวกับ TLS

ข้อมูล TLS มาในบล็อก

ก่อนหน้านี้ฉันคิดว่าเมื่อคุณสร้างการเชื่อมต่อแล้ว ข้อมูล TLS ที่เข้ารหัสนั้นเป็นเพียงสตรีม แต่นั่นไม่ใช่วิธีการทำงาน – แทนที่จะส่งเป็นบล็อก เช่น คุณจะได้อันนึงประมาณ 1,400 ไบต์เพื่อถอดรหัส แล้วก็อีกอัน แล้วก็อีกอันหนึ่ง

ฉันไม่แน่ใจว่าทำไมบล็อกถึงมีขนาดเท่ากัน (บางทีก็เพื่อให้แต่ละบล็อกพอดีในแพ็กเก็ต TCP ???) แต่ในทางทฤษฎี ฉันคิดว่าพวกมันอาจมีขนาดสูงสุด 65535 ไบต์ เนื่องจากฟิลด์ขนาดของพวกเขาคือ 2 ไบต์ บล็อกที่ฉันได้รับคือแต่ละบล็อก 1386 ไบต์

ทุกครั้งที่เราได้รับบล็อก เราต้อง:

  • คำนวณ IV ใหม่เป็น old_iv xor num_records_received
  • ถอดรหัสโดยใช้คีย์และ IV . ใหม่
  • เพิ่มจำนวนบันทึกที่ได้รับ

นี่คือหน้าตาของฟังก์ชัน ReceiveData() ที่ฉันเขียน

ส่วนที่น่าสนใจที่สุดคือ iv[11] ^= session.RecordsReceived – นั่นคือส่วนที่ปรับ IV สำหรับแต่ละบล็อก

 func (session *Session) ReceiveData() []byte { record := readRecord(session.Conn) iv := make([]byte, 12) copy(iv, session.Keys.ServerApplicationIV) iv[11] ^= session.RecordsReceived plaintext := decrypt(session.Keys.ServerApplicationKey, iv, record) session.RecordsReceived += 1 return plaintext }

iv[11] นี้ถือว่ามีน้อยกว่า 255 บล็อกซึ่งเห็นได้ชัดว่าไม่เป็นความจริงใน TLS ทั่วไป แต่ฉันขี้เกียจและดาวน์โหลดหน้าแรกของบล็อกฉันต้องการเพียง 82 บล็อก

เราต้องทำสิ่งนี้เมื่อเราส่งข้อมูลด้วย แต่ฉันไม่ได้ใช้งานเพราะเราส่งเพียง 1 แพ็กเก็ต

ปัญหา: รับทั้งบล็อกของข้อมูล tLS

ฉันพบปัญหาหนึ่งกับ TCP ซึ่งบางครั้งฉันพยายามอ่านบล็อกข้อมูล TLS (~1386 ไบต์) แต่ฉันจะไม่เข้าใจทั้งหมด ฉันเดาว่าบล็อก TLS สามารถแบ่งออกเป็นหลายแพ็กเก็ต TCP

ฉันแก้ไขปัญหานี้ด้วยวิธีที่โง่มาก โดยเพียงแค่สำรวจการเชื่อมต่อ TCP แบบวนซ้ำจนกว่าจะให้ข้อมูลที่ฉันต้องการ นี่คือรหัสของฉันที่จะทำ:

 func read(length int, reader io.Reader) []byte { var buf []byte for len(buf) != length { buf = append(buf, readUpto(length-len(buf), reader)...) } return buf }

ฉันถือว่าการนำ TLS ไปใช้จริงจะใช้เธรดพูลหรือ coroutines หรือบางอย่างเพื่อจัดการสิ่งนี้

ขั้นตอนที่ 10: รู้ว่าเมื่อเราทำเสร็จแล้ว

เมื่อการตอบสนอง HTTP เสร็จสิ้น เราได้รับไบต์เหล่านี้: []byte{48, 13, 10, 13, 10, 23} ฉันไม่รู้ว่าสิ่งนี้หมายความว่าอย่างไร แต่ดูเหมือนว่าจะส่งสัญญาณการสิ้นสุดการเชื่อมต่อ

นี่คือรหัสที่จะได้รับการตอบสนอง HTTP โดยพื้นฐานแล้วเราแค่วนซ้ำจนกว่าเราจะเห็นไบต์เหล่านั้นแล้วเราก็หยุด

 func (session *Session) ReceiveHTTPResponse() []byte { var response []byte for { pt := session.ReceiveData() if string(pt) == string([]byte{48, 13, 10, 13, 10, 23}) { break } response = append(response, pt...) } return response }

แค่นั้นแหละ!

ในที่สุด ฉันรันโปรแกรมและดาวน์โหลดหน้าแรกของบล็อกของฉัน! มันได้ผล! ผลลัพธ์จะมีลักษณะดังนี้:

 $ go build; ./tiny-tls HTTP/1.1 200 OK Date: Wed, 23 Mar 2022 19:37:47 GMT Content-Type: text/html Transfer-Encoding: chunked Connection: keep-alive ... lots more headers and HTML follow...

โอเค ผลลัพธ์เป็นแบบต่อต้านไคลแมกซ์ มันเหมือนกับสิ่งที่คุณเห็นถ้าคุณรัน curl -i https://jvns.ca ยกเว้นว่าไม่มีการจัดรูปแบบ แต่ฉันรู้สึกตื่นเต้นมากเมื่อฉันเห็นมัน

ตัวบล็อคมันแปลกๆ

ฉันพบว่ามันค่อนข้างแปลกที่ข้อมูล TLS ถูกส่ง/รับเป็นบล็อค ฉันคิดว่ามันสมเหตุสมผลจากมุมมองของการเข้ารหัส (เพราะคุณต้องการเปลี่ยน IV ของคุณบ่อยๆหรืออย่างอื่น?)

แต่จากมุมมองของเครือข่าย มันรู้สึกแปลกๆ เล็กน้อย เช่นเดียวกับ TCP ถูกสร้างขึ้นบนระบบแพ็คเก็ต จากนั้นด้วย TLS คุณจะมีระบบแพ็คเก็ตอื่นที่ด้านบนของ TCP

บางทีหนึ่งในแรงจูงใจสำหรับ QUIC คือการออกแบบโปรโตคอลเครือข่ายที่มี TLS เป็นพลเมืองชั้นหนึ่งมากกว่า ดังนั้นคุณไม่มีแพ็กเก็ต/สตรีม/แพ็กเก็ตแปลก ๆ นี้ใช่ไหม

บางสิ่งที่ฉันได้เรียนรู้

มันสนุกจริงๆ! ฉันได้เรียนรู้ว่า

  • เส้นโค้งวงรี diffie-hellman นั้นเจ๋งมากและอย่างน้อยกับ Curve25519 คุณสามารถใช้สตริง 32 ไบต์ใดก็ได้เป็นคีย์ส่วนตัว
  • มีคีย์สมมาตรที่แตกต่างกันมากมายที่เกี่ยวข้องกับ TLS และกระบวนการได้มาซึ่งคีย์นั้นค่อนข้างซับซ้อน
  • TLS ใช้ AES พร้อมอัลกอริธึม “การเข้ารหัสที่ตรวจสอบความถูกต้อง” พิเศษอยู่ด้านบน
  • ข้อมูล TLS ถูกส่ง/รับเป็นกลุ่มของบล็อก ไม่ใช่เป็นสตรีม

ฉันจะไม่แสร้งทำเป็นว่าเข้าใจเหตุผลทั้งหมด TLS ที่ออกแบบมาด้วยวิธีนี้ แต่มันเป็นวิธีที่สนุกที่จะใช้เวลาสองสามวัน ฉันรู้สึกมีข้อมูลมากขึ้น และฉันคิดว่ามันจะง่ายขึ้นสำหรับฉันที่จะเข้าใจสิ่งที่ฉัน อ่านเกี่ยวกับ TLS ในอนาคต

เฉลิมฉลองเหตุการณ์สำคัญแห่งการเรียนรู้เล็กๆ

สวัสดี! วันนี้ฉันอยากจะพูดถึง – คุณรู้ได้อย่างไรว่าคุณเขียนโปรแกรมได้ดีขึ้น?

แนวทางหนึ่งที่ชัดเจนคือ:

  1. ทำเป้าหมาย
  2. ตรวจสอบเป็นระยะๆ ว่าคุณบรรลุเป้าหมายเหล่านั้นหรือไม่
  3. ถ้าคุณทำ เฉลิมฉลอง

ฉันเกลียดเป้าหมาย

เป้าหมายอาจมีประโยชน์ แต่หลายครั้งที่ฉันพบว่ามันเครียดและไม่มีประโยชน์ ตัวอย่างเช่น นี่คือเป้าหมายสองสามข้อที่ฉันจดไว้สำหรับตัวเองเมื่อ 9 ปีที่แล้ว:

  • เขียนโค้ด C จำนวนไม่น้อยที่ใช้งานได้จริงและคนทั่วไปใช้
  • มีส่วนร่วมในโครงการโอเพ่นซอร์สในC
  • เรียนรู้ C++

9 ปีต่อมา ฉันได้ทำ 0 สิ่งเหล่านั้น ด้วยการวางกรอบ “เป้าหมาย” ให้มองว่าสิ่งนี้เป็นสิ่งที่ไม่ดี! เช่น ฉันต้องการเรียนรู้ C++ และไม่ได้ทำ! ยังเขียน C ไม่ได้อยู่ดี! ไม่นะ! ฉันเดาว่าฉันล้มเหลว!

ฉันพบว่าการวางกรอบนี้น่าหดหู่และไม่ช่วยเหลือ อันที่จริง ฉันไม่มีเหตุผลที่แท้จริงในการเรียนรู้ C++ ในขณะนั้น และตอนนี้ก็ยังไม่มี ดังนั้นจึงทำให้รู้สึกว่าฉันไม่ได้เรียนรู้มัน

ฉันชอบคิดถึง เหตุการณ์สำคัญเล็กๆ แทนเป้าหมาย

เหตุการณ์สำคัญคืออะไร?

โดยปกติเมื่อเราพูดถึงเหตุการณ์สำคัญ เราหมายถึงสิ่งที่ยิ่งใหญ่ เช่น “ฉันจบการศึกษาจากมหาวิทยาลัย”

แต่ในโพสต์นี้ ฉันต้องการพูดถึงเหตุการณ์สำคัญในแง่ของนิรุกติศาสตร์ – หิน ที่วางทุก ไมล์ บนทางหลวง เพื่อให้คุณสามารถติดตามความคืบหน้าของคุณไปตามเส้นทาง

สิ่งเหล่านี้เกิดขึ้นบ่อยขึ้นมาก – บางทีคุณอาจใช้เครื่องมือใหม่เป็นครั้งแรก หรือคุณแก้ไขข้อผิดพลาดประเภทใหม่ที่คุณไม่เคยเห็นมาก่อน หรือคุณได้เรียนรู้เกี่ยวกับแนวคิดใหม่!

เหตุการณ์สำคัญ C เล็ก ๆ ของฉันบางส่วน

ต่อไปนี้คือตัวอย่างเล็กๆ น้อยๆ ของเหตุการณ์สำคัญเล็กๆ น้อยๆ จาก 9 ปีที่ผ่านมาที่เกี่ยวข้องกับเป้าหมาย “เรียนรู้ภาษา C/C++” ดั้งเดิมของฉัน

  • เขียนเชลล์พื้นฐานมากในC
  • เขียนโมดูลเคอร์เนลลินุกซ์ขนาดเล็ก
  • เรียนรู้เกี่ยวกับ strace
  • เรียนรู้วิธีจัดระเบียบไบนารีของ ELF (สัญลักษณ์ ส่วนต่างๆ ฯลฯ)
  • เรียนรู้ว่าสัญลักษณ์การดีบัก DWARF คืออะไรและทำงานอย่างไร
  • พบข้อผิดพลาดเคอร์เนล Mac และจัดการเพื่อ เขียนโปรแกรมทำซ้ำในC
  • เรียนรู้วิธีการใช้ gdb เพื่อตรวจสอบหน่วยความจำของโปรแกรม C
  • ใช้การใช้ประโยชน์จากบัฟเฟอร์ล้นโดยใช้ gdb และ strace (สำหรับ CTF)
  • เขียนว่า คอมพิวเตอร์เป็นเกมที่รวดเร็ว
  • ได้รับคอร์ดัมพ์สำหรับโปรแกรม C ++ ที่ขัดข้องและจัดการเพื่อดึงสแต็กเทรซออกมา
  • เขียนรายงานจุดบกพร่องที่ถูกต้องสำหรับโปรแกรม C++ ที่มีหน่วยความจำรั่ว ซึ่งส่งผลให้ผู้ดูแลแก้ไขจุดบกพร่อง
  • เรียนรู้เกี่ยวกับรูปแบบ RAII (แม้ว่าจะอยู่ใน Rust ไม่ใช่ C ++)
  • จับคู่โปรแกรมกับเพื่อนเกี่ยวกับการนำปัญหา Advent of Code ไปใช้ในแอสเซมบลี x86
  • โดยทั่วไปแล้ว ฉันสามารถเขียนโปรแกรม C พื้นฐานได้สบาย ๆ ตราบใดที่พวกเขาไม่ต้องทำอะไรแฟนซีเช่น “การจัดการหน่วยความจำ”

และยังมีเหตุการณ์สำคัญบางอย่างเช่นที่ฉันเขียน Ruby profiler ใน Rust ในปี 2018

เมื่อฉันคิดแบบนี้ ฉันรู้สึกดีกับทักษะของฉัน! ฉันได้เรียนรู้ทุกสิ่งที่เกี่ยวข้องกับการเขียนโปรแกรมระบบ มันเพิ่งเกิดขึ้นในวิธีที่ต่างไปจากที่ฉันคาดไว้ในตอนแรก

การเปลี่ยนเป้าหมายไม่ใช่เรื่องเลวร้าย

เหตุผลที่ฉันยังไม่ได้เรียน C นั้นไม่ใช่ว่าฉันห่วยหรือ C นั้นเป็นไปไม่ได้ที่จะเรียนรู้ แค่การเรียนรู้วิธีเขียน C ให้ดีไม่เคยเป็นสิ่งที่ฉันมีเหตุผลจริงๆ ที่ต้องทำ

แต่ฉันได้เรียนรู้ Rust and Go และ strace และ gdb และเกี่ยวกับโครงสร้างและสัญลักษณ์ C และ call stack และ heap และสิ่งอื่น ๆ มากมาย (อีกอย่าง ผมชอบบทความนี้มาก บางคนก็มีความหมายสำหรับ C ว่าทำไม C ถึงยังสำคัญอยู่)

และนั่นก็ใช้ได้ผลดี! ดังนั้น ฉันคิดว่าการมีความยืดหยุ่นเกี่ยวกับเป้าหมายและการเฉลิมฉลองเป้าหมายที่คุณทำสำเร็จจะดีขึ้นมาก แทนที่จะรู้สึกแย่กับเป้าหมายที่คุณ “ล้มเหลว”

เฉลิมฉลองเหตุการณ์สำคัญเล็กๆ ของคุณ

การ ฉลอง เหตุการณ์สำคัญเล็กๆ น้อยๆ เช่นนี้มีประโยชน์มากสำหรับฉัน ฉันเฉลิมฉลองอย่างมากด้วยการเขียนโพสต์บนบล็อก – ฉันเขียนรายการด้านบนโดยส่วนใหญ่โดยดูจากรายชื่อโพสต์บนบล็อกเก่าของฉันสำหรับสิ่งที่ฉันเขียนเกี่ยวกับเรื่องที่เกี่ยวข้องกับ C

ถ้าคุณไม่เขียนบล็อก (ไม่ใช่สำหรับทุกคนแน่นอน!) การเขียนสิ่งนี้ลงใน เอกสารคุยโม้ แทนอาจเป็นประโยชน์

แต่ฉันคิดว่ามันสำคัญที่จะเฉลิมฉลองเหตุการณ์สำคัญเหล่านี้ ที่ไหนสักแห่ง มันให้ความรู้สึกที่แท้จริงว่าฉันกำลังก้าวหน้าและช่วยให้ฉันมีแรงจูงใจที่จะเรียนรู้เกี่ยวกับสิ่งนั้นต่อไป

วิธีใช้เว็บ API ที่ไม่มีเอกสาร

สวัสดี! สองสามวันที่ฉันเขียนเกี่ยวกับ โปรแกรมส่วนตัวเล็กๆ และบอกว่าการใช้ API ที่ไม่มีเอกสาร “ลับ” เป็นเรื่องสนุก ซึ่งคุณต้องคัดลอกคุกกี้ออกจากเบราว์เซอร์เพื่อเข้าถึง

มีคนถามวิธีทำสิ่งนี้ ดังนั้นฉันจึงต้องการอธิบายว่าอย่างไร เพราะมันค่อนข้างตรงไปตรงมา นอกจากนี้ เราจะพูดคุยกันเล็กน้อยเกี่ยวกับสิ่งที่อาจผิดพลาด ปัญหาด้านจริยธรรม และวิธีที่สิ่งนี้ใช้กับ API ที่ไม่มีเอกสารของคุณ

ตัวอย่างเช่น ลองใช้ Google แฮงเอาท์ ฉันเลือกสิ่งนี้ไม่ใช่เพราะมันเป็นตัวอย่างที่มีประโยชน์ที่สุด (ฉันคิดว่ามี API อย่างเป็นทางการซึ่งน่าจะใช้งานได้จริงมากกว่า) แต่เนื่องจากไซต์จำนวนมากที่มีประโยชน์จริง ๆ คือไซต์ขนาดเล็กกว่าที่เสี่ยงต่อการละเมิดมากกว่า ดังนั้นเราจะใช้ Google แฮงเอาท์เพราะฉันแน่ใจ 100% ว่าแบ็กเอนด์ของ Google แฮงเอาท์ได้รับการออกแบบให้มีความยืดหยุ่นในการพูดคุยในลักษณะนี้

มาเริ่มกันเลย!

ขั้นตอนที่ 1: ค้นหาเครื่องมือสำหรับนักพัฒนาเพื่อหาคำตอบ JSON ที่มีแนวโน้ม

ฉันเริ่มต้นด้วยการไปที่ https://hangouts.google.com เปิดแท็บเครือข่ายในเครื่องมือสำหรับนักพัฒนา Firefox และค้นหาคำตอบ JSON คุณสามารถใช้เครื่องมือสำหรับนักพัฒนา Chrome ได้เช่นกัน

หน้าตาประมาณนี้

network-tab.png

คำขอเป็นตัวเลือกที่ดีหากมีคำว่า “json” ในคอลัมน์ “ประเภท”

ฉันต้องมองไปรอบๆ สักพักจนกระทั่งพบสิ่งที่น่าสนใจ แต่ในที่สุดฉันก็พบจุดปลาย “ผู้คน” ที่ดูเหมือนจะส่งคืนข้อมูลเกี่ยวกับผู้ติดต่อของฉัน ฟังดูน่าสนุก เรามาลองดูกัน

ขั้นตอนที่ 2: คัดลอกเป็น cURL

ต่อไป ฉันคลิกขวาที่คำขอที่ฉันสนใจ แล้วคลิก “คัดลอก” -> “คัดลอกเป็น cURL”

จากนั้นฉันวางคำสั่ง curl ในเทอร์มินัลแล้วเรียกใช้ นี่คือสิ่งที่เกิดขึ้น

 $ curl 'https://people-pa.clients6.google.com/v2/people/?key=REDACTED' -X POST ........ (a bunch of headers removed) Warning: Binary output can mess up your terminal. Use "--output -" to tell Warning: curl to output it to your terminal anyway, or consider "--output Warning: <FILE>" to save to a file.

คุณอาจจะกำลังคิด – ที่แปลก ข้อผิดพลาด “เอาต์พุตไบนารีนี้อาจทำให้เทอร์มินัลของคุณยุ่งเหยิง” คืออะไร นั่นเป็นเพราะโดยค่าเริ่มต้น เบราว์เซอร์จะส่ง Accept-Encoding: gzip, deflate header ไปยังเซิร์ฟเวอร์ เพื่อรับเอาต์พุตที่บีบอัด

เราสามารถแตกไฟล์ได้โดยการไพพ์เอาต์พุตไปที่ gunzip แต่ฉันคิดว่ามันง่ายกว่าที่จะไม่ส่งส่วนหัวนั้น เรามาลบส่วนหัวที่ไม่เกี่ยวข้องกัน

ขั้นตอนที่ 3: ลบส่วนหัวที่ไม่เกี่ยวข้อง

นี่คือบรรทัดคำสั่ง curl แบบเต็มที่ฉันได้รับจากเบราว์เซอร์ มีมากที่นี่! ฉันเริ่มต้นด้วยการแยกคำขอด้วยแบ็กสแลช ( \ ) เพื่อให้แต่ละส่วนหัวอยู่ในบรรทัดที่แตกต่างกันเพื่อให้ทำงานได้ง่ายขึ้น:

 curl 'https://people-pa.clients6.google.com/v2/people/?key=REDACTED' \ -X POST \ -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0' \ -H 'Accept: */*' \ -H 'Accept-Language: en' \ -H 'Accept-Encoding: gzip, deflate' \ -H 'X-HTTP-Method-Override: GET' \ -H 'Authorization: SAPISIDHASH REDACTED' \ -H 'Cookie: REDACTED' -H 'Content-Type: application/x-www-form-urlencoded' \ -H 'X-Goog-AuthUser: 0' \ -H 'Origin: https://hangouts.google.com' \ -H 'Connection: keep-alive' \ -H 'Referer: https://hangouts.google.com/' \ -H 'Sec-Fetch-Dest: empty' \ -H 'Sec-Fetch-Mode: cors' \ -H 'Sec-Fetch-Site: same-site' \ -H 'Sec-GPC: 1' \ -H 'DNT: 1' \ -H 'Pragma: no-cache' \ -H 'Cache-Control: no-cache' \ -H 'TE: trailers' \ --data-raw 'personId=101777723309&personId=1175339043204&personId=1115266537043&personId=116731406166&extensionSet.extensionNames=HANGOUTS_ADDITIONAL_DATA&extensionSet.extensionNames=HANGOUTS_OFF_NETWORK_GAIA_GET&extensionSet.extensionNames=HANGOUTS_PHONE_DATA&includedProfileStates=ADMIN_BLOCKED&includedProfileStates=DELETED&includedProfileStates=PRIVATE_PROFILE&mergedPersonSourceOptions.includeAffinity=CHAT_AUTOCOMPLETE&coreIdParams.useRealtimeNotificationExpandedAcls=true&requestMask.includeField.paths=person.email&requestMask.includeField.paths=person.gender&requestMask.includeField.paths=person.in_app_reachability&requestMask.includeField.paths=person.metadata&requestMask.includeField.paths=person.name&requestMask.includeField.paths=person.phone&requestMask.includeField.paths=person.photo&requestMask.includeField.paths=person.read_only_profile_info&requestMask.includeField.paths=person.organization&requestMask.includeField.paths=person.location&requestMask.includeField.paths=person.cover_photo&requestMask.includeContainer=PROFILE&requestMask.includeContainer=DOMAIN_PROFILE&requestMask.includeContainer=CONTACT&key=REDACTED'

นี้อาจดูเหมือนจำนวนมากของสิ่งต่างๆ ในตอนแรก แต่คุณไม่จำเป็นต้องคิดว่ามันหมายถึงอะไรในขั้นตอนนี้ คุณเพียงแค่ต้องลบบรรทัดที่ไม่เกี่ยวข้อง

ฉันมักจะคิดออกว่าส่วนหัวใดที่ฉันสามารถลบได้ด้วยการลองผิดลองถูก – ฉันจะลบส่วนหัวออกไปเรื่อยๆ จนกว่าคำขอจะเริ่มล้มเหลว โดยทั่วไป คุณอาจไม่ต้องการ Accept* , Referer , Sec-* , DNT , User-Agent และแคชส่วนหัว

ในตัวอย่างนี้ ฉันสามารถตัดคำขอเป็นดังนี้:

 curl 'https://people-pa.clients6.google.com/v2/people/?key=REDACTED' \ -X POST \ -H 'Authorization: SAPISIDHASH REDACTED' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -H 'Origin: https://hangouts.google.com' \ -H 'Cookie: REDACTED'\ --data-raw 'personId=101777723309&personId=1175339043204&personId=1115266537043&personId=116731406166&extensionSet.extensionNames=HANGOUTS_ADDITIONAL_DATA&extensionSet.extensionNames=HANGOUTS_OFF_NETWORK_GAIA_GET&extensionSet.extensionNames=HANGOUTS_PHONE_DATA&includedProfileStates=ADMIN_BLOCKED&includedProfileStates=DELETED&includedProfileStates=PRIVATE_PROFILE&mergedPersonSourceOptions.includeAffinity=CHAT_AUTOCOMPLETE&coreIdParams.useRealtimeNotificationExpandedAcls=true&requestMask.includeField.paths=person.email&requestMask.includeField.paths=person.gender&requestMask.includeField.paths=person.in_app_reachability&requestMask.includeField.paths=person.metadata&requestMask.includeField.paths=person.name&requestMask.includeField.paths=person.phone&requestMask.includeField.paths=person.photo&requestMask.includeField.paths=person.read_only_profile_info&requestMask.includeField.paths=person.organization&requestMask.includeField.paths=person.location&requestMask.includeField.paths=person.cover_photo&requestMask.includeContainer=PROFILE&requestMask.includeContainer=DOMAIN_PROFILE&requestMask.includeContainer=CONTACT&key=REDACTED'

ดังนั้นฉันต้องการแค่ 4 ส่วนหัว: Authorization , Content-Type , Origin และ Cookie ที่สามารถจัดการได้มากขึ้น

ขั้นตอนที่ 4: แปลเป็น Python

ตอนนี้เรารู้แล้วว่าเราต้องการ header อะไร เราก็สามารถแปลคำสั่ง curl เป็นโปรแกรม Python ได้! ส่วนนี้ยังเป็นกระบวนการทางกลที่ค่อนข้างสวย เป้าหมายคือการส่งข้อมูลเดียวกันกับ Python เหมือนกับที่เราเคยทำกับ curl

นี่คือสิ่งที่ดูเหมือน สิ่งนี้เหมือนกับคำสั่ง curl ก่อนหน้าทุกประการ แต่ใช้ requests ของ Python ฉันยังแยกสตริงเนื้อหาคำขอที่ยาวมากออกเป็นอาร์เรย์ของทูเพิลเพื่อให้ทำงานด้วยการเขียนโปรแกรมได้ง่ายขึ้น

 import requests import urllib data = [ ('personId','101777723'), # I redacted these IDs a bit too ('personId','117533904'), ('personId','111526653'), ('personId','116731406'), ('extensionSet.extensionNames','HANGOUTS_ADDITIONAL_DATA'), ('extensionSet.extensionNames','HANGOUTS_OFF_NETWORK_GAIA_GET'), ('extensionSet.extensionNames','HANGOUTS_PHONE_DATA'), ('includedProfileStates','ADMIN_BLOCKED'), ('includedProfileStates','DELETED'), ('includedProfileStates','PRIVATE_PROFILE'), ('mergedPersonSourceOptions.includeAffinity','CHAT_AUTOCOMPLETE'), ('coreIdParams.useRealtimeNotificationExpandedAcls','true'), ('requestMask.includeField.paths','person.email'), ('requestMask.includeField.paths','person.gender'), ('requestMask.includeField.paths','person.in_app_reachability'), ('requestMask.includeField.paths','person.metadata'), ('requestMask.includeField.paths','person.name'), ('requestMask.includeField.paths','person.phone'), ('requestMask.includeField.paths','person.photo'), ('requestMask.includeField.paths','person.read_only_profile_info'), ('requestMask.includeField.paths','person.organization'), ('requestMask.includeField.paths','person.location'), ('requestMask.includeField.paths','person.cover_photo'), ('requestMask.includeContainer','PROFILE'), ('requestMask.includeContainer','DOMAIN_PROFILE'), ('requestMask.includeContainer','CONTACT'), ('key','REDACTED') ] response = requests.post('https://people-pa.clients6.google.com/v2/people/?key=REDACTED', headers={ 'X-HTTP-Method-Override': 'GET', 'Authorization': 'SAPISIDHASH REDACTED', 'Content-Type': 'application/x-www-form-urlencoded', 'Origin': 'https://hangouts.google.com', 'Cookie': 'REDACTED', }, data=urllib.parse.urlencode(data), ) print(response.text)

ฉันรันโปรแกรมนี้และใช้งานได้ – มันพิมพ์ JSON ออกมาจำนวนมาก! ไชโย!

คุณจะสังเกตเห็นว่าฉันได้แทนที่หลายอย่างด้วย REDACTED นั่นเป็นเพราะถ้าฉันรวมค่าเหล่านั้นไว้ คุณจะสามารถเข้าถึง Google Hangouts API สำหรับบัญชีของฉันได้ ซึ่งคงจะไม่ดี

และเสร็จแล้ว!

ตอนนี้ ฉันสามารถปรับเปลี่ยนโปรแกรม Python ให้ทำทุกอย่างที่ต้องการได้ เช่น ส่งผ่านพารามิเตอร์ต่างๆ หรือแยกวิเคราะห์ผลลัพธ์

ฉันจะไม่ทำอะไรที่น่าสนใจกับมันเพราะฉันไม่สนใจที่จะใช้ API นี้เลย ฉันแค่ต้องการแสดงให้เห็นว่ากระบวนการนี้เป็นอย่างไร

แต่เราได้ JSON จำนวนหนึ่งกลับมาซึ่งคุณสามารถทำอะไรได้อย่างแน่นอน

curlconverter ดูดีมาก

มีคนแสดงความคิดเห็นว่าคุณสามารถแปล curl เป็น Python ได้ (และภาษาอื่นๆ อีกมาก!) โดยอัตโนมัติด้วย https://curlconverter.com/ ซึ่งดูดีมาก ฉันเคยทำด้วยตนเองมาโดยตลอด ฉันลองใช้ตัวอย่างนี้และดูเหมือนว่าจะใช้งานได้ดี

การหาว่า API ทำงานอย่างไรนั้นไม่สำคัญ

ฉันไม่ต้องการที่จะขีดเส้นใต้ว่ายากแค่ไหนที่จะเข้าใจว่า API ที่ไม่รู้จักทำงานอย่างไร – ไม่ชัดเจน! ฉันไม่รู้ว่าพารามิเตอร์ของ Google Hangouts API นี้ทำอะไรได้บ้าง!

แต่บ่อยครั้งที่มีพารามิเตอร์บางอย่างที่ดูตรงไปตรงมา เช่น requestMask.includeField.paths=person.email อาจหมายถึง “รวมที่อยู่อีเมลของแต่ละคน” ดังนั้นฉันจึงพยายามมุ่งเน้นไปที่พารามิเตอร์ที่ ฉัน เข้าใจมากกว่าที่ฉัน ไม่ เข้าใจ

สิ่งนี้ใช้ได้เสมอ (ในทางทฤษฎี)

บางท่านอาจสงสัยว่า คุณทำสิ่งนี้ได้ตลอดเวลาหรือไม่

คำตอบคือใช่ – เบราว์เซอร์ไม่ใช่สิ่งมหัศจรรย์! เบราว์เซอร์ข้อมูลทั้งหมดที่ส่งไปยังแบ็กเอนด์ของคุณเป็นเพียงคำขอ HTTP ดังนั้น ถ้าฉันคัดลอกส่วนหัว HTTP ทั้งหมดที่เบราว์เซอร์ของฉันส่ง ไม่มีทางที่แบ็กเอนด์จะบอกได้อย่างแท้จริงว่าเบราว์เซอร์ของฉัน ไม่ได้ ส่งคำขอและถูกส่งโดยโปรแกรม Python แบบสุ่ม

แน่นอน เราได้ลบส่วนหัวจำนวนหนึ่งที่เบราว์เซอร์ส่งออกไป ดังนั้นในทางทฤษฎีแล้ว แบ็กเอนด์ สามารถ บอกได้ แต่โดยปกติแล้วจะไม่ตรวจสอบ

มีข้อแม้บางประการ เช่น บริการของ Google จำนวนมากมีแบ็กเอนด์ที่สื่อสารกับฟรอนต์เอนด์ด้วยวิธีที่ไม่อาจเข้าใจได้ (สำหรับฉัน) ดังนั้นแม้ว่าในทางทฤษฎีแล้ว คุณสามารถเลียนแบบสิ่งที่พวกเขาทำ แต่ในทางปฏิบัติอาจเกือบ เป็นไปไม่ได้.

ตอนนี้เราได้เห็นวิธีการใช้ API ที่ไม่มีเอกสารในลักษณะนี้แล้ว มาพูดถึงบางสิ่งที่อาจผิดพลาดกันได้

ปัญหาที่ 1: คุกกี้เซสชั่นหมดอายุ

ปัญหาใหญ่อย่างหนึ่งคือฉันใช้คุกกี้เซสชัน Google เพื่อตรวจสอบสิทธิ์ ดังนั้นสคริปต์นี้จะหยุดทำงานทุกครั้งที่เซสชันเบราว์เซอร์ของฉันหมดอายุ

นั่นหมายความว่าวิธีนี้ใช้ไม่ได้กับโปรแกรมที่ใช้เวลานาน (ฉันต้องการใช้ API จริง) แต่ถ้าฉันต้องการดึงข้อมูลเล็กน้อยอย่างรวดเร็วเพียงครั้งเดียวก็ใช้งานได้ดี !

ปัญหาที่ 2: การล่วงละเมิด

ถ้าฉันใช้เว็บไซต์ขนาดเล็ก มีโอกาสที่สคริปต์ Python ตัวน้อยของฉันสามารถหยุดบริการได้ เพราะมันส่งคำขอมากกว่าที่พวกเขาจะรับมือได้ ดังนั้นเมื่อฉันทำสิ่งนี้ ฉันพยายามที่จะเคารพและไม่ร้องขอมากเกินไปเร็วเกินไป

นี่เป็นสิ่งสำคัญโดยเฉพาะอย่างยิ่งเนื่องจากไซต์จำนวนมากที่ไม่มี API อย่างเป็นทางการเป็นไซต์ขนาดเล็กที่มีทรัพยากรน้อยกว่า

ในตัวอย่างนี้เห็นได้ชัดว่านี่ไม่ใช่ปัญหา ฉันคิดว่าฉันได้ส่งคำขอทั้งหมด 20 รายการไปยังแบ็กเอนด์ของ Google แฮงเอาท์ขณะเขียนบล็อกโพสต์นี้ ซึ่งพวกเขาสามารถจัดการได้อย่างแน่นอน

นอกจากนี้ หากคุณใช้ข้อมูลประจำตัวของบัญชีเพื่อเข้าถึง API ในลักษณะที่มากเกินไปและก่อให้เกิดปัญหา คุณอาจถูกระงับบัญชี (สมเหตุสมผลมาก)

ฉันยังยึดติดกับการดาวน์โหลดข้อมูลที่เป็นของฉันหรือตั้งใจให้เข้าถึงได้แบบสาธารณะ – ฉันไม่ได้ค้นหาช่องโหว่

อย่าลืมว่าทุกคนสามารถใช้ API ที่ไม่มีเอกสารของคุณได้

ฉันคิดว่าสิ่งสำคัญที่สุดที่ต้องรู้เกี่ยวกับเรื่องนี้ไม่ใช่วิธีใช้ API ที่ไม่มีเอกสาร ของ ผู้อื่น มันสนุกที่จะทำ แต่มีข้อจำกัดมากมาย และฉันไม่ได้ทำบ่อยขนาดนั้น

การเข้าใจว่าใครๆ ก็ทำสิ่งนี้กับแบ็กเอนด์ API ของคุณ ได้นั้นสำคัญกว่ามาก! ทุกคนมีเครื่องมือสำหรับนักพัฒนาและแท็บเครือข่าย และมันค่อนข้างง่ายที่จะดูว่าพารามิเตอร์ใดที่คุณส่งไปยังแบ็กเอนด์และเปลี่ยนแปลงได้

ดังนั้น ถ้าใครสามารถเปลี่ยนพารามิเตอร์บางอย่างเพื่อรับข้อมูลของผู้ใช้รายอื่นได้ ก็ไม่ดี ฉันคิดว่านักพัฒนาส่วนใหญ่ที่สร้าง API ที่เปิดเผยต่อสาธารณะรู้เรื่องนี้ดี แต่ฉันกำลังพูดถึงเพราะทุกคนจำเป็นต้องเรียนรู้มันเป็นครั้งแรกในบางประเด็น 🙂

โปรแกรมส่วนตัวเล็ก ๆ ที่ฉันเขียนขึ้น

ฉันกำลังคุยกับเพื่อนเมื่อฤดูร้อนที่แล้วเกี่ยวกับแหล่งข้อมูลที่อาจเป็นประโยชน์สำหรับคนที่กำลังเรียนรู้การเขียนโปรแกรม เพื่อนของฉันบอกว่าพวกเขาคิดว่าบางคนอาจได้ประโยชน์จากรายการโครงการเล็กๆ น้อยๆ ที่สนุกสนาน ซึ่งเป็นสิ่งที่คุณสามารถทำได้ในตอนเย็นหรือวันหยุดสุดสัปดาห์

เลยมาว่ากัน! ฉันชอบเขียนโปรแกรมขนาดเล็กที่มีประโยชน์ในชีวิตของฉัน แบบนี้:

  • อา! ปัญหาเล็กๆ น้อยๆ ในชีวิต!
  • ฉันรู้ ฉันพนันได้เลยว่าฉันสามารถแก้ปัญหานี้ด้วย CODE เย้.
  • การเขียนโปรแกรมแห่งความสุข 4 ชั่วโมงจึงบังเกิด

นี่ไม่ใช่วิธี ปฏิบัติ ที่ได้ผลที่สุดเสมอไป (ปัญหามากมายที่ฉันแก้ไขด้วยการเขียนโปรแกรมสามารถแก้ไขได้โดยใช้เวลาน้อยลงในรูปแบบอื่น) แต่ตราบใดที่เป้าหมายของคุณคือการเขียนโปรแกรมให้สนุกและโปรแกรมของคุณก็ไม่เสียหาย ฉันคิดว่านี่เป็นแนวทางที่ดี 🙂

ต่อไปนี้คือตัวอย่างเล็กๆ น้อยๆ ของโครงการเขียนโปรแกรมส่วนตัวเล็กๆ ที่ฉันทำ ฉันจะไม่พูดถึง “โครงการเรียนรู้” ที่เป้าหมายของฉันคือการเรียนรู้บางอย่างที่เฉพาะเจาะจงเพราะฉันได้เขียนบล็อกโพสต์เกี่ยวกับเรื่องนั้นไปแล้วนับพันล้านโพสต์

สิ่งเหล่านี้เป็นมากกว่าแค่การทำสิ่งที่สนุกโดยไม่มีเป้าหมายการเรียนรู้ที่เฉพาะเจาะจง

เทศกาลละครไม่มีปฏิทิน

Fringe Festival ในท้องถิ่นมีการแสดงมากมาย แต่ไม่มีที่ใดที่ฉันจะดูปฏิทินได้ในหน้าเดียว ดังนั้นฉันจึงเขียนสคริปต์ Python เพื่อขูดเว็บไซต์และสร้างปฏิทิน นี่คือ รหัส และ ผลลัพธ์

การพิมพ์ปกหนังสือขนาดเล็ก

เมื่อเร็ว ๆ นี้ฉันเห็น วิดีโอ TikTok ที่มีคนสร้าง ebook เวอร์ชันจริงขนาดเล็กที่พวกเขาอ่าน ฉันตัดสินใจลองใช้มัน ดังนั้นฉันจึงต้องพิมพ์ปกหนังสือรุ่นเล็กๆ ฉันสามารถปรับขนาดทั้งหมดได้ด้วยตนเอง แต่ฉันตัดสินใจที่จะทำด้วยการเขียนโปรแกรมแทน

ดังนั้นฉันจึงเขียน HTML และ CSS เล็กน้อย ( tinybooks.html ) แปลงเป็น PDF แล้วพิมพ์ออกมา

ทำให้เครื่องสแกนของฉันทำงานได้ดีขึ้น

นี่เป็นเพียง “การเขียนโปรแกรม” แต่ฉันต้องสแกนเอกสารจำนวนมากสำหรับสมาชิกในครอบครัว และฉันไม่ชอบซอฟต์แวร์ที่มีอยู่ ดังนั้นฉันจึงเขียน เชลล์สคริปต์แรปเปอร์ขนาดเล็กสำหรับ scanimage เพื่อให้กระบวนการง่ายขึ้น สิ่งนี้ช่วยฉันได้มากจริง ๆ และฉันยังคงใช้มันเมื่อสแกน

นัดรับวัคซีน

เมื่อเปิดโดสวัคซีนโควิดครั้งที่สอง ทุกช่องก็เต็ม ปรากฎว่าแบ็กเอนด์ของเว็บไซต์มี API ดังนั้นฉันจึงเขียนสคริปต์เพื่อสำรวจ API ทุกๆ 60 วินาทีและดูการยกเลิกและแจ้งให้ฉันทราบเพื่อที่ฉันจะได้นัดหมายก่อนหน้านี้

สิ่งนี้ไม่จำเป็น (มีการนัดหมายมากขึ้นในไม่ช้าอย่างไรก็ตาม) แต่ก็สนุก

กำลังดูข้อมูลตลาดที่อยู่อาศัย

เรากำลังคิดจะซื้อคอนโดเมื่อสองสามปีที่แล้ว และฉันก็โกรธที่หาข้อมูลราคาในอดีตไม่ได้เลย ดังนั้นฉันจึงเขียนโน้ตบุ๊ก iPython ที่สอบถาม API ของเว็บไซต์อสังหาริมทรัพย์ในท้องถิ่นเพื่อขูดข้อมูลและคำนวณบางส่วน สถิติเช่นราคาต่อตารางฟุตเมื่อเวลาผ่านไป

ฉันไม่คิดว่าสิ่งนี้ช่วยเราได้จริงๆ ในการซื้อคอนโด แต่มันสนุก

(“การใช้ API ของบริการในพื้นที่” ดูเหมือนจะเป็นธีมต่อเนื่อง สิ่งหนึ่งที่ฉันชอบคือการใช้ API ที่ไม่มีเอกสารที่เป็นความลับ ซึ่งคุณต้องคัดลอกคุกกี้ของคุณออกจากเบราว์เซอร์เพื่อเข้าถึง)

นามบัตรอักษรไขว้

ในปี 2013 ฉันคิดว่าการมีนามบัตรที่เป็นปริศนาอักษรไขว้ที่ฉันสนใจอาจเป็นเรื่องที่สนุก ดังนั้นฉันจึงเขียนซอฟต์แวร์ทั่วไปเพื่อ สร้างปริศนาอักษรไขว้จากไฟล์ข้อความ ฉันค่อนข้างแน่ใจว่าไม่เคยพิมพ์นามบัตรแต่เขียนได้สนุก

การทำซองจดหมาย

ก่อนหน้านี้ฉันกำลังส่งนิตยสารบางฉบับ และตัดสินใจว่าฉันต้องการพิมพ์ป้ายชื่อแบบกำหนดเองบนซองจดหมายทุกฉบับ ซึ่งเป็นสถานการณ์แบบ “จดหมายเวียน” ดังนั้นฉันจึงเขียนโปรแกรม Python เพื่ออ่านที่อยู่ทางไปรษณีย์ทั้งหมด และสร้าง HTML และ CSS จากนั้นฉันก็เปลี่ยน HTML/CSS เป็น PDF และพิมพ์ซองจดหมาย สิ่งนี้ใช้ได้ผลดี

สำรวจรูปแบบการทอยลูกเต๋า

เพื่อนแสดงให้ฉันเห็นเกมทอยลูกเต๋าที่คุณทอยลูกเต๋าและเพิ่มค่า ฉันกล่าวว่าถ้าคุณทอยลูกเต๋ามากพอและเพิ่มค่าทั้งหมด ในบางจุด มันก็จะ “สุ่ม” น้อยลงมาก

แต่แล้วฉันต้องการดูว่าสุ่มได้มากน้อยเพียงใด ดังนั้นฉันจึงเขียนโปรแกรมเล็กๆ เพื่อทอยลูกเต๋า 2,500 ลูก และบวกผลรวมหลายๆ ครั้งเพื่อดูว่ามันทำงานอย่างไร (สมมุติว่าคุณสามารถคำนวณสิ่งเดียวกันกับคณิตศาสตร์ได้ แต่โค้ดจะง่ายกว่า)

นี่เป็นโค้ดเล็กๆ น้อยๆ ฉันจะใส่ไว้ในบรรทัดนี้ (มันคือไพธอน). นี่คือ [ผลลัพธ์] ( https://gist.github.com/jvns/e4a35ca2bad90c1a0fcaf578a803b456

 import random def roll(): return sum(random.randint(1, 6) for i in range(2500)) while True: print(roll())

รับภาพวาดในแอพ Notability

ฉันใช้แอพที่ชื่อว่า Squid เพื่อวาดรูป และฉันกำลังเปลี่ยนไปใช้ Notability และต้องการนำภาพวาดเก่าของฉันมาสู่ Notability ดังนั้นฉันจึง ออกแบบรูปแบบไฟล์ Notability แบบย้อนกลับ

ฉันไม่คิดว่าในที่สุดสิ่งนี้จะมีประโยชน์ (ในที่สุดฉันก็เปลี่ยนไปใช้แอพวาดรูปอื่นซึ่งมีการนำเข้า SVG จริง) แต่ฉันก็สนุก

ปิดรีทวิต

นี่เป็นโครงการเล็ก ๆ น้อย ๆ เล็กน้อย (ใช้เวลามากกว่าหนึ่งวัน) แต่ฉันตัดสินใจว่าไม่ต้องการเห็นรีทวีตบน Twitter อีกต่อไป ดังนั้นฉันจึงเขียน เว็บไซต์ขนาดเล็ก เพื่อที่ฉันจะได้ปิดการรีทวีต

ฉันรักโครงการเล็ก ๆ จริงๆ

ตัวอย่างทั้งหมดเหล่านี้เป็นข้อมูลที่ใหม่กว่า แต่ฉันคิดว่าเมื่อฉันเริ่มเรียนรู้การเขียนโปรแกรมโครงการเล็กๆ น้อยๆ ที่มีเดิมพันน้อยๆ แบบนี้ช่วยฉันได้จริงๆ ฉันชอบแบบนั้น

  • พวกเขาเป็นเพียงสำหรับฉัน (ถ้ามันผิดพลาดก็ไม่เป็นไร!)
  • ฉันสามารถทำมันให้เสร็จในตอนเย็นหรือวันหยุดสุดสัปดาห์ (ไม่ใช่สิ่งยักษ์ที่ห้อยอยู่บนหัวของฉัน)
  • ถ้ามันได้ผล ก็มีผลงานที่จับต้องได้ในชีวิตฉัน (เช่น ซองจดหมายหรือหนังสือย่อส่วน หรือกำหนดการนามบัตรหรือประสบการณ์ Twitter ที่ดีขึ้น)

บางสิ่งเกี่ยวกับ getaddrinfo ที่ทำให้ฉันประหลาดใจ

สวัสดี! ต่อไปนี้คือบางสิ่งที่คุณอาจหรืออาจไม่ได้สังเกตเกี่ยวกับ DNS:

  • เมื่อคุณแก้ไขชื่อ DNS ในโปรแกรม Python โปรแกรมจะตรวจสอบ /etc/hosts แต่เมื่อคุณใช้ dig จะไม่ตรวจสอบ
  • การเปลี่ยนการกระจาย Linux ในบางครั้งสามารถเปลี่ยนวิธีการทำงานของ DNS ของคุณได้ ตัวอย่างเช่น หากคุณใช้ Alpine Linux แทน Ubuntu ก็อาจทำให้เกิดปัญหาได้
  • Mac OS มีการแคช DNS แต่ Linux ไม่จำเป็น เว้นแต่คุณจะใช้ systemd-resolved หรือบางอย่าง

เพื่อทำความเข้าใจสิ่งเหล่านี้ เราจำเป็นต้องเรียนรู้เกี่ยวกับฟังก์ชันที่เรียกว่า getaddrinfo ซึ่งมีหน้าที่รับผิดชอบในการค้นหา DNS

มีหลายสิ่งที่น่าประหลาดใจสำหรับฉันเกี่ยวกับ getaddrinfo และเมื่อฉันได้เรียนรู้เกี่ยวกับสิ่งเหล่านี้ มันจะอธิบายพฤติกรรม DNS ที่สับสนหลายอย่างที่ฉันเคยเห็นในอดีต

getaddrinfo มาจากไหน?

getaddrinfo เป็นส่วนหนึ่งของไลบรารีที่เรียกว่า libc ซึ่งเป็นไลบรารี C มาตรฐาน มี libc อย่างน้อย 3 เวอร์ชัน:

  1. glibc (GNU libc)
  2. musl libc
  3. libc เวอร์ชัน Mac OS (ฉันไม่รู้ว่าชื่อนี้มีหรือเปล่า)

มีอีกมากอย่างแน่นอน (ฉันคิดว่า FreeBSD และ OpenBSD ต่างก็มีเวอร์ชันของตัวเอง เป็นต้น) แต่นั่นคือ 3 สิ่งที่ฉันรู้

แต่ละคนมี getaddrinfo เวอร์ชันของตนเอง

ไม่ใช่ทุกโปรแกรมที่ใช้ getaddrinfo สำหรับ DNS

สิ่งแรกที่ฉันพบว่าน่าประหลาดใจคือ getaddrinfo มีการใช้กันอย่างแพร่หลายแต่ไม่ได้ใช้อย่างแพร่หลาย

ทุกโปรแกรมมีพื้นฐาน 2 ตัวเลือก:

  1. ใช้ getaddrinfo ฉันคิดว่า Python, Ruby และ Node ใช้ getaddrinfo เช่นเดียวกับ Go ในบางครั้ง อาจมีหลายภาษามากกว่านี้ด้วย แต่ฉันไม่มีเวลาค้นหาไลบรารี DNS ของทุกภาษา
  2. ใช้ฟังก์ชันตัวแก้ไข DNS ที่กำหนดเอง ตัวอย่างของสิ่งนี้:
    • ขุด. ฉันคิดว่าเป็นเพราะ dig ต้องการการควบคุมมากกว่าการสืบค้น DNS มากกว่าที่ getaddrinfo รองรับ ดังนั้นจึงใช้ตรรกะ DNS ของตัวเอง
    • Go ยังมีตัวแก้ไข DNS แบบ Pure-Go หากคุณไม่ต้องการใช้CGo
    • มี Ruby gem ที่มี ตัวแก้ไข DNS แบบกำหนดเอง ที่คุณสามารถใช้เพื่อแทนที่ getaddrinfo
    • getaddrinfo ไม่รองรับ DNS ผ่าน HTTPS ดังนั้นฉันคิดว่าเบราว์เซอร์ที่ใช้ DoH ไม่ได้ใช้ getaddrinfo สำหรับการค้นหา DNS เหล่านั้น
    • คงอีกมากที่ฉันไม่รู้

บางครั้งคุณจะเห็น getaddrinfo ในข้อความแสดงข้อผิดพลาด DNS ของคุณ

เนื่องจากมีการใช้ getaddrinfo อย่างแพร่หลาย คุณจึงมักจะเห็นในข้อความแสดงข้อผิดพลาดที่เกี่ยวข้องกับ DNS

ตัวอย่างเช่น ถ้าฉันเรียกใช้โปรแกรม Python ซึ่งค้นหาชื่อโดเมนที่ไม่มีอยู่:

 import requests requests.get("http://xyxqqx.com")

ฉันได้รับข้อความแสดงข้อผิดพลาดนี้:

 Traceback (most recent call last): File "/usr/lib/python3.10/site-packages/urllib3/connection.py", line 174, in _new_conn conn = connection.create_connection( File "/usr/lib/python3.10/site-packages/urllib3/util/connection.py", line 72, in create_connection for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): File "/usr/lib/python3.10/socket.py", line 955, in getaddrinfo for res in _socket.getaddrinfo(host, port, family, type, proto, flags): socket.gaierror: [Errno -2] Name or service not known

ฉันคิดว่า socket.getaddrinfo กำลังเรียก libc getaddrinfo ที่ไหนสักแห่งภายใต้ประทุน แม้ว่าฉันไม่ได้อ่านซอร์สโค้ดทั้งหมดเพื่อตรวจสอบ

ก่อนที่คุณจะเรียนรู้ว่า getaddrinfo คืออะไร ไม่ชัดเจนเลยที่ socket.gaierror: [Errno -2] Name or service not known หมายความว่า “ไม่มีโดเมนนั้น” มันไม่แม้แต่จะพูดคำว่า “DNS” หรือ “โดเมน” ได้ทุกที่!

getaddrinfo บน Mac ไม่ได้ใช้ /etc/resolv.conf

ฉันเคยใช้ Mac ในการทำงาน และมักจะรู้สึกไม่มั่นคงโดย DNS บน Mac ฉันสามารถบอกได้ว่ามี บางอย่าง ที่แตกต่างจากวิธีการทำงานบนเครื่อง Linux ของฉัน แต่ฉันไม่รู้ว่ามันคืออะไร

ฉันยังไม่เข้าใจสิ่งนี้ทั้งหมด และเป็นการยากสำหรับฉันที่จะตรวจสอบ เพราะขณะนี้ฉันไม่สามารถเข้าถึง Mac ได้ แต่นี่คือสิ่งที่ฉันได้รวบรวมมาจนถึงตอนนี้

บนระบบ Linux getaddrinfo ตัดสินใจว่าตัวแก้ไข DNS ตัวใดที่จะพูดคุยกับโดยใช้ไฟล์ชื่อ /etc/resolv.conf (เห็นได้ชัดว่ามีความซับซ้อนเพิ่มเติมบางอย่างกับ /etc/nsswitch.conf แต่ฉันไม่เคยดู /etc/nsswitch.conf ดังนั้นฉันจะไม่สนใจมัน)

ตัวอย่างเช่น นี่คือเนื้อหาของ /etc/resolv.conf ของฉันตอนนี้:

 # Generated by NetworkManager nameserver 192.168.1.1 nameserver fd13:d987:748a::1

ซึ่งหมายความว่าในการสอบถาม DNS นั้น getaddrinfo จะส่งคำขอไปที่ 192.168.1.1 บนพอร์ต 53 นั่นคือตัวแก้ไข DNS ของเราเตอร์ของฉัน

ฉันคิดว่านี่เป็น getaddrinfo บน Mac และเพิ่งใช้ /etc/resolv.conf แต่ฉันคิดผิด แต่ getaddrinfo ส่งคำขอไปยังโปรแกรมที่เรียกว่า mDNSResponder ซึ่งเป็นของ Mac

ฉันไม่รู้อะไรมากเกี่ยวกับ mDNSResponder ยกเว้นว่าทำการแคช DNS และเห็นได้ชัดว่าคุณสามารถล้างแคชด้วย dscacheutil ได้ สิ่งนี้อธิบายความลึกลับอย่างหนึ่งในตอนต้นของโพสต์ – เหตุใด Macs จึงมีการแคช DNS และเครื่อง Linux ไม่เสมอไป

musl libc getaddrinfo นั้นแตกต่างจากเวอร์ชันของ glibc

คุณอาจคิดว่าโอเค Mac OS getaddrinfo ต่างกัน แต่ getaddrinfo สองเวอร์ชันใน glibc และ musl libc ส่วนใหญ่จะต้องเหมือนกันใช่ไหม

แต่พวกเขามีความแตกต่างที่สำคัญทีเดียว ความแตกต่างหลักที่ฉันรู้คือ musl libc ไม่รองรับ TCP DNS ฉันไม่พบสิ่งใดในเอกสารเกี่ยวกับมัน แต่มีการกล่าวถึงใน ทวีตนี้ )

ฉันได้พูดเพิ่มเติมเกี่ยวกับสิ่งนี้ TCP DNS ใน ลักษณะที่ DNS สามารถทำลาย ได้

ความแตกต่างเพิ่มเติมบางประการ:

  • วิธีจัดการโดเมนการค้นหา (ใน /etc/resolv.conf ) แตกต่างกันเล็กน้อย ( กล่าวถึงที่นี่ )
  • โพสต์นี้ระบุว่า musl ไม่รองรับ nsswitch.conf ฉันไม่เคยใช้ nsswitch.conf และไม่รู้ว่าทำไมมันถึงมีประโยชน์ แต่ฉันคิดว่ามีเหตุผลที่ฉันไม่รู้

สิ่งแปลก ๆ เพิ่มเติม: nscd?

เมื่อค้นหา getaddrinfo ฉันยังพบ โพสต์ที่น่าสนใจเกี่ยวกับ getaddrinfo จาก James Fisher ที่ติดตาม glibc getaddrinfo และพบว่าเห็นได้ชัดว่าเรียกโปรแกรมบางตัวที่เรียกว่า nscd ซึ่งควรจะทำการแคช DNS โพสต์บล็อกนั้นอธิบาย nscd ว่า “ไม่เสถียร” และ “ออกแบบมาไม่ดี” และฉันไม่ชัดเจนว่ามีการใช้กันอย่างแพร่หลายเพียงใด

ฉันไม่รู้อะไรเกี่ยวกับ nscd แต่ฉันตรวจสอบแล้ว และเห็นได้ชัดว่ามันอยู่ในคอมพิวเตอร์ของฉัน ฉันลองแล้วนี่คือสิ่งที่เกิดขึ้น:

 $ nscd child exited with status 4

ความประทับใจของฉันคือผู้ที่ต้องการแคช DNS บน Linux มักจะใช้ตัวส่งต่อ DNS เช่น dnsmasq หรือ systemd-resolved resolved แทนที่จะใช้ nscd นั่นคือสิ่งที่ฉันเห็นในอดีต

นั่นคือทั้งหมด!

เมื่อฉันเรียนรู้เกี่ยวกับทั้งหมดนี้เป็นครั้งแรก ฉันพบว่ามันน่าแปลกใจจริงๆ ที่ฟังก์ชันไลบรารีที่ใช้กันอย่างแพร่หลายนั้นมีพฤติกรรมที่แตกต่างกันออกไปบนแพลตฟอร์มต่างๆ

ฉันหมายความว่า มันสมเหตุสมผลแล้วที่คนที่สร้าง Mac OS จะต้องการจัดการแคช DNS ด้วยวิธีที่ต่างไปจากที่จัดการบน Linux ดังนั้นจึงสมเหตุสมผลที่พวกเขาใช้ getaddrinfo ต่างกัน และมันก็สมเหตุสมผลที่บางโปรแกรมเลือกที่จะไม่ใช้ getaddrinfo เพื่อทำการสืบค้น DNS

แต่มันทำให้ DNS ยากขึ้นเล็กน้อยที่จะให้เหตุผล

การเขียนโปรแกรมสิ่งต่าง ๆ ที่ง่ายขึ้น

สวัสดี! วันก่อนฉันคุยกับเพื่อนบางคนเกี่ยวกับประเภทของการสนทนาในการประชุมใหญ่ที่เราชอบ

หมวดหมู่หนึ่งที่เราคิดขึ้นมาคือ “คุณรู้จักสิ่งนี้ที่เคยยากสุด ๆ ไหม? กลายเป็นว่าตอนนี้ง่ายกว่ามาก และคุณอาจจะทำได้ตอนนี้!“.

ฉันก็เลยถามใน Twitter เกี่ยวกับ การเขียนโปรแกรมที่เมื่อก่อนยากและตอนนี้กลับง่าย

นี่คือคำตอบบางส่วนที่ฉันได้รับ ไม่ใช่ว่าทุกคนจะ “ง่าย” เท่ากัน แต่ฉันพบว่าการอ่านรายการสนุกจริงๆ และให้แนวคิดบางอย่างสำหรับสิ่งที่ต้องเรียนรู้ บางทีมันอาจจะให้ความคิดกับคุณด้วย

  • ใบรับรอง SSL พร้อม Let’s Encrypt
  • การทำงานพร้อมกัน โดยมี async/await (ในหลายภาษา)
  • จัดกึ่งกลางใน CSS ด้วย flexbox/grid
  • สร้างโปรแกรมที่รวดเร็วด้วย Go/Rust
  • การจดจำภาพด้วย การเรียนรู้การถ่ายโอน (มีคนชี้ให้เห็นว่าเรื่องตลกใน XKCD นี้ไม่สมเหตุสมผลอีกต่อไป)
  • สร้าง GUI ข้ามแพลตฟอร์มด้วย Electron
  • VPN พร้อม Wireguard
  • เรียกใช้รหัสของคุณเองภายในเคอร์เนล Linux ด้วย eBPF
  • การคอมไพล์ข้าม (Go และ Rust เรือพร้อมการสนับสนุนการคอมไพล์ข้ามนอกกรอบ)
  • การกำหนดค่าโครงสร้างพื้นฐานระบบคลาวด์ด้วย Terraform
  • การตั้งค่าสภาพแวดล้อมสำหรับนักพัฒนาด้วย Docker
  • แบ่งปันหน่วยความจำอย่างปลอดภัยกับเธรดด้วย Rust
  • นิพจน์ทั่วไปทำให้ง่ายขึ้นเล็กน้อยด้วยเครื่องมืออย่าง regexr และ regexpr

สิ่งที่เกี่ยวข้องกับบริการโฮสต์:

  • CI/CD พร้อมด้วย GitHub Actions/CircleCI/GitLab เป็นต้น
  • สร้างเว็บไซต์ที่มีประโยชน์ด้วยการเขียนโค้ดส่วนหน้าพร้อมบริการแบ็กเอนด์ “ไร้เซิร์ฟเวอร์” ที่หลากหลาย
  • ฝึกโครงข่ายประสาทเทียมด้วย Colab
  • การปรับใช้เว็บไซต์กับเซิร์ฟเวอร์ด้วย Netlify/Heroku เป็นต้น
  • การเรียกใช้ฐานข้อมูลด้วยบริการที่เป็นโฮสต์ เช่น RDS
  • เว็บแอปพลิเคชันแบบเรียลไทม์ด้วย Firebase

สิ่งที่ฉันยังไม่ได้ทำ แต่นั่นฟังดูเจ๋ง:

  • การเข้ารหัสด้วย crypto ดั้งเดิมที่มีความเห็นเช่น libsodium
  • อัปเดตสดสำหรับหน้าเว็บที่ส่งโดยเว็บเซิร์ฟเวอร์ด้วย LiveView/Hotwire
  • การเขียนโปรแกรมแบบฝังด้วย MicroPython
  • สร้างวิดีโอเกมด้วย Roblox / Unity
  • การเขียนโค้ดที่ทำงานบน GPU ในเบราว์เซอร์ (อาจเป็นด้วย Unity?)
  • การสร้างเครื่องมือ IDE ด้วย LSP (โปรโตคอลเซิร์ฟเวอร์ภาษา)
  • ผู้พิสูจน์ทฤษฎีบทเชิงโต้ตอบ (ไม่แน่ใจว่าอะไร)
  • การแยกวิเคราะห์ด้วยไลบรารี PEG
  • ไมโครคอนโทรลเลอร์ ESP
  • ประมวลผลข้อมูลเป็นชุดด้วย Spark
  • การจดจำรูปภาพด้วยบริการ ML ที่โฮสต์ เช่น Teachable Machine

ภาษาเฉพาะสิ่งที่ผู้คนกล่าวถึง:

  • ขึ้นสนิมตลอดอายุการใช้งาน
  • IE รองรับ CSS/JS

ความหมายที่หลากหลายของ “เนมเซิร์ฟเวอร์” และ “ตัวแก้ไข DNS”

ตอนนี้ฉันกำลังทำนิตยสารเกี่ยวกับ DNS ดังนั้นฉันจึงคิดเกี่ยวกับคำศัพท์ DNS มากกว่าคนทั่วไป นี่คือสิ่งที่สับสนเล็กน้อยที่ฉันสังเกตเห็นเกี่ยวกับคำศัพท์ DNS!

เงื่อนไขเซิร์ฟเวอร์ DNS ทั่วไปสองคำ (“เนมเซิร์ฟเวอร์” และ “ตัวแก้ไข DNS”) มีความหมายต่างกันไปตามสถานการณ์

นี่ไม่ใช่ปัญหาหากคุณเข้าใจวิธีการทำงานของ DNS แล้ว – ฉันสามารถทราบได้อย่างง่ายดายว่า “เนมเซิร์ฟเวอร์” ประเภทใดกำลังถูกกล่าวถึงตามบริบท

แต่อาจเป็นปัญหาได้หากคุณกำลังพยายามเรียนรู้วิธีการทำงานของ DNS และคุณไม่ทราบว่าคำเหล่านั้นอาจหมายถึงสิ่งต่าง ๆ ขึ้นอยู่กับบริบท – มันน่าสับสน! ฉันจะอธิบายความหมายที่เป็นไปได้ต่างๆ และวิธีหาความหมายที่ตั้งใจไว้

2 ความหมายของ “เนมเซิร์ฟเวอร์”

เนมเซิร์ฟเวอร์มีอยู่ 2 ประเภท และคำว่า “เนมเซิร์ฟเวอร์” หมายถึงประเภทใดขึ้นอยู่กับบริบท

ความหมาย 1: เนมเซิร์ฟเวอร์ “เผด็จการ”

เมื่อคุณอัปเดตระเบียน DNS สำหรับโดเมน ระเบียนเหล่านั้นจะถูกเก็บไว้ในเซิร์ฟเวอร์ที่เรียกว่า nameserver ที่เชื่อถือได้

นี่คือสิ่งที่ “เนมเซิร์ฟเวอร์” หมายถึงในบริบทของ โดเมนเฉพาะ นี่คือตัวอย่างบางส่วน:

  • “เชื่อมต่อโดเมนที่คุณเป็นเจ้าของกับ Wix แล้วด้วยการเปลี่ยน เนมเซิร์ฟเวอร์
  • “เกือบทุกโดเมนอาศัยเนม เซิร์ฟเวอร์ หลายตัวเพื่อเพิ่มความน่าเชื่อถือ: หากเนมเซิร์ฟเวอร์หนึ่งหยุดทำงานหรือไม่พร้อมใช้งาน การสืบค้น DNS สามารถไปที่อื่นได้”
  • “คุณสามารถอัปเดตระเบียนเนม เซิร์ฟเวอร์ ได้ด้วยตัวเองโดยทำตามขั้นตอนที่ผู้รับจดทะเบียนโดเมนของคุณอาจระบุไว้ในเนื้อหาความช่วยเหลือที่เว็บไซต์ของตน”

ความหมาย 2. เนมเซิร์ฟเวอร์ “แบบเรียกซ้ำ” หรือที่เรียกว่า “ตัวแก้ไข DNS”

เซิร์ฟเวอร์เหล่านี้แคชระเบียน DNS เบราว์เซอร์ของคุณไม่ได้ส่งคำขอไปยังเนมเซิร์ฟเวอร์ที่เชื่อถือได้โดยตรง แต่จะส่งคำขอไปยังตัวแก้ไข DNS (หรือที่เรียกว่าเนมเซิร์ฟเวอร์แบบเรียกซ้ำ) ซึ่งจะค้นหาว่าเซิร์ฟเวอร์ชื่อที่ถูกต้องที่จะพูดคุยคืออะไร รับบันทึก และแคชผลลัพธ์

นี่คือสิ่งที่ “เนมเซิร์ฟเวอร์” หมายถึงในบริบทของ การท่องอินเทอร์เน็ต ของคุณ (“เนมเซิร์ฟเวอร์ของคอมพิวเตอร์ของคุณ”) นี่คือตัวอย่างบางส่วน:

  • “การเปลี่ยน เนมเซิร์ฟเวอร์ อาจเป็นเรื่องยุ่งยากในอุปกรณ์บางอย่างและต้องคลิกหลายครั้งผ่านอินเทอร์เฟซผู้ใช้ บน Windows 10 ตัวอย่างเช่น…”
  • เนมเซิร์ฟเวอร์ DNS ของคุณขัดขวางการใช้งานอินเทอร์เน็ตของคุณหรือไม่? NEW RELEASE เพิ่มเนมเซิร์ฟเวอร์ 1.1.1.1, 1.0.0.1 และ 9.9.9.9”
  • “กำหนดการตั้งค่าเครือข่ายของคุณเพื่อใช้ที่อยู่ IP 8.8.8.8 และ 8.8.4.4 เป็นเซิร์ฟเวอร์ DNS ของคุณ”

ฉันชอบใช้คำว่า “ตัวแก้ไข DNS” มากกว่า แม้ว่าจะมี 2 ความหมาย เพราะมันมักใช้มากกว่า “เนมเซิร์ฟเวอร์แบบเรียกซ้ำ”

ความหมายของ “ตัวแก้ไข DNS”

ตัวแก้ไข DNS สามารถเป็นไลบรารีหรือเซิร์ฟเวอร์ก็ได้ (ขออภัย ฉันรู้ว่าฉันบอกว่าตัวแก้ไข DNS เป็นเซิร์ฟเวอร์ก่อนหน้านี้ แต่บางครั้งก็เป็นห้องสมุด)

ความหมาย 1a: “ตัวแก้ไขต้นขั้ว” (เวอร์ชันไลบรารี)

“ตัวแก้ไขต้นขั้ว” เป็นสิ่งที่ (อาจเป็นไลบรารีหรือเซิร์ฟเวอร์ DNS) ซึ่งไม่ทราบวิธีแก้ไขชื่อ DNS เอง แต่มีหน้าที่ส่งต่อการสืบค้น DNS ไปยังตัวแก้ไข DNS “ของจริง” มาพูดถึงตัวแก้ไขต้นขั้วที่เป็นไลบรารีก่อน

ตัวอย่างเช่น ฟังก์ชัน getaddrinfo จาก libc ไม่ทราบวิธีค้นหาระเบียน DNS เอง เพียงแต่รู้ว่าต้องค้นหาใน /etc/resolv.conf และส่งต่อการสืบค้นไปยังเซิร์ฟเวอร์ DNS ใดก็ตามที่พบ

คุณจะทราบได้อย่างไรว่าสิ่งนี้มีความหมาย: หากเป็นส่วนหนึ่งของระบบปฏิบัติการคอมพิวเตอร์ของคุณและ/หรือเป็นไลบรารี แสดงว่าเป็นโปรแกรมแก้ไขต้นขั้ว

ตัวอย่างของความหมายของ “ตัวแก้ไข DNS” นี้:

  • ตัวแก้ไข คือชุดของรูทีนในไลบรารี C ที่ให้การเข้าถึง Internet Domain Name System (DNS)”
  • “นี่คือเซิร์ฟเวอร์ DNS ที่ใช้ในการแก้ไขที่อยู่เว็บ คุณสามารถระบุรายการได้มากถึงสามรายการ และ โปรแกรมแก้ไข จะลองใช้แต่ละรายการทีละรายการจนกว่าจะพบรายการที่ใช้งานได้”
  • “หากคำสั่งสำเร็จ คุณจะได้รับข้อความต่อไปนี้ “ล้าง DNS Resolver Cache สำเร็จ””

ความหมาย 1b: “ตัวแก้ไขต้นขั้ว” (เวอร์ชันเซิร์ฟเวอร์)

ตัวแก้ไขต้นขั้วนั้นไม่ใช่ไลบรารีเสมอไป เช่น systemd-resolved resolved และ dnsmasq เป็นตัวแก้ไขต้นขั้ว แต่เป็นเซิร์ฟเวอร์ เราเตอร์ของคุณอาจใช้งาน dnsmasq

สิ่งนี้เรียกอีกอย่างว่า “ตัวส่งต่อ DNS”

คุณจะทราบได้อย่างไรว่าสิ่งนี้มีความหมาย: หากเราเตอร์ของคุณใช้งานอยู่หรือเป็นส่วนหนึ่งของระบบปฏิบัติการ แสดงว่าอาจเป็นตัวแก้ไขต้นขั้ว

ความหมาย 2: เนมเซิร์ฟเวอร์แบบเรียกซ้ำ (เซิร์ฟเวอร์)

“เนมเซิร์ฟเวอร์แบบเรียกซ้ำ” (เช่นที่เราพูดถึงก่อนหน้านี้) คือเซิร์ฟเวอร์ที่รู้วิธีค้นหาเนมเซิร์ฟเวอร์ที่เชื่อถือได้สำหรับโดเมน นี่คือตัวแก้ไข DNS ชนิดหนึ่งที่ฉันกำลังพูดถึงใน ตัวแก้ไข DNS ของเล่น ที่โพสต์เมื่อสองสามสัปดาห์ก่อน (แม้ว่าของฉันไม่ใช่เซิร์ฟเวอร์)

จะทราบได้อย่างไรว่ามีความหมาย: ถ้ามัน unbound , bind , 8.8.8.8, 1.1.1.1 หรือเรียกใช้โดย ISP ของคุณ แสดงว่าเป็นเนมเซิร์ฟเวอร์แบบเรียกซ้ำ

ตัวอย่างของความหมายของ “ตัวแก้ไข DNS” นี้:

  • “ DNS Resolver ในซอฟต์แวร์ pfSense® ใช้งานแบบไม่ผูกมัด ซึ่งเป็น ตัวแก้ไข DNS ที่ตรวจสอบความถูกต้อง เรียกซ้ำ และแคช …”
  • “เราขอเชิญคุณลองใช้ Google Public DNS เป็น ตัวแก้ไข DNS หลักหรือรองของคุณ…”
  • “ฉันทำงานให้กับผู้ให้บริการมือถือรายใหญ่พอสมควร และเราอยู่ในขั้นตอนการติดตั้งตัว แก้ไข DNS ของเราเอง…”

คำเซิร์ฟเวอร์ DNS ยอดนิยม

ฉันยังทำแบบสำรวจตามหลักวิทยาศาสตร์อย่างรวดเร็วว่าคำที่ใช้อ้างอิงเซิร์ฟเวอร์ DNS นั้นใช้บ่อยที่สุดโดยการนับผลลัพธ์ของ Google นี่คือสิ่งที่ฉันพบ:

  • เซิร์ฟเวอร์ DNS: 8,000,000
  • เนมเซิร์ฟเวอร์: 4,200,000
  • ตัวแก้ไข DNS: 933,000
  • เซิร์ฟเวอร์ DNS สาธารณะ: 204,000
  • เนมเซิร์ฟเวอร์: 42,000
  • ตัวแก้ไขแบบเรียกซ้ำ: 38,500
  • ตัวแก้ไขต้นขั้ว: 26,100
  • เนมเซิร์ฟเวอร์ที่เชื่อถือได้: 17,000
  • บริการความละเอียด DNS: 9,450
  • เนมเซิร์ฟเวอร์ TLD: 7,500
  • เคอร์เซอร์ DNS: 5,300
  • เนมเซิร์ฟเวอร์แบบเรียกซ้ำ: 5,060

โดยพื้นฐานแล้วสิ่งนี้บอกฉันว่าด้วยระยะขอบที่ค่อนข้างใหญ่ คำที่นิยมใช้มากที่สุดเมื่อพูดถึงการเสิร์ฟ DNS คือ “เนมเซิร์ฟเวอร์” และ “ตัวแก้ไข DNS”

คำเฉพาะเจาะจงมากขึ้น เช่น “เนมเซิร์ฟเวอร์แบบเรียกซ้ำ”, “เนมเซิร์ฟเวอร์ที่เชื่อถือได้” และ “ตัวแก้ไขต้นขั้ว” มีน้อยกว่ามาก

นั่นคือทั้งหมด!

ฉันหวังว่านี่จะช่วยให้บางคนเข้าใจว่าคำเหล่านี้หมายถึงอะไร! คำศัพท์ดูสับสนกว่าที่ฉันต้องการ แต่ดูเหมือนว่าฉันจะอธิบายได้ดีกว่าการใช้ภาษาที่คลุมเครือน้อยกว่าซึ่งไม่ได้ใช้กันทั่วไปในทางปฏิบัติ