<< Personal Blog

June 2026 Wrap Up

Deep diving into Odin, building a DNS resolver, and betraying my Vim roots for Emacs.

This month was really busy, so I feel like I didn’t get much programming done. However, when I looked back at my GitHub commits, I had 14 days of contributions—close to half the month—so I was still able to work on something, which I’m happy with. Earlier this month I decided to go all in on Odin, so I built a DNS resolver for fun, and I’m betraying my Vim roots and trying out Emacs…

Going All In on Odin

I’ve been jumping around trying to figure out what I want to focus on to progress in my programming journey. Up until this point—after finishing the first half of the Boot.dev course, which covered the fundamentals of programming—I’ve been trying to spread wide and cover many different languages and topics instead of going deep on any one thing. This has been great up to this point because I’ve cast a wide net, so I know a little bit about a lot.

However, now that I have more of a general understanding of programming, I’ve decided to full send into Odin and systems-level programming. I’ve found that area really interesting, so I’m just going to go all in and see what happens. This decision was sparked because I didn’t get the job I interviewed for, which is completely understandable. That experience made me realize I still have so much to learn, and that it’s better to go deep and niche rather than wide if you don’t have a formal degree in programming.

Building a DNS Resolver

With going all in on Odin, I wanted to learn more about systems-level networking, so I built a DNS resolver. Computers only know how to read IP addresses when it comes to networking, so when you hand it, let’s say, google.com, it doesn’t know what to do with that—it passes it to the resolver. What the resolver then does is reach out to the directory (.com, for example), and that directory points to the domain, which then returns the IP address—142.250.113.101 is one of Google’s addresses.

I don’t remember all the details of how I built this since it was just a day project, but I did have to learn how to send a DNS packet, which meant looking at the RFC 1035 document to understand how to structure it. A DNS packet has five sections: Header, Question, Answer, Authority, and Additional. For the basic query I built, I only needed to concern myself with the header and the question. Then, for the received packet, I needed to parse the answer section.

Building the Header

The header is twelve bytes total, broken down into six rows of two bytes each. The first row is the ID—it can be any 16-bit number, so I used u16(rand.uint32()) to generate a random u16 integer. The next row is the flags, which I’m not going to go into because I don’t fully understand them myself yet. If I revisit this project and build it out more, I plan to dig into the full scope of what they do. Rows 3-6 are the counts, which are just standard answer counts, so I kept those simple.

Building the Question

The question has three sections: the domain name (encoded as length-prefixed labels), QTYPE, and QCLASS. The most difficult part of building the question was parsing the domain name. From the header, I kept a running count of which byte I was on, so I built a function to parse the domain by passing in the domain, the buffer, and my current position. I needed to split the domain by the periods, then get the length of each section—google(.)com. I then encoded the length as a number first, followed by each letter of that section, so it ends up looking like 6 g o o g l e 3 c o m 0. You close it out with a zero to signal that you’re done with the domain. As I’m doing this, I’m incrementing the position counter for each character. So all that in code looks like this. I’m sure there’s a better way to write it, but this is how my brain figured it out:

encode_name :: proc(domain: string, buf: []u8, offset: int) -> int {
  pos := offset
  domain_split, err := strings.split(domain, ".")
  if err != .None {
    return -1
  }
  for label in domain_split {
    if pos >= len(buf) {
      return -1
    }
    buf[pos] = u8(len(label))
    pos += 1
    for char in label {
      if pos >= len(buf) {
        return -1
      }
      buf[pos] = u8(char)
      pos += 1
    }
  }
  if pos >= len(buf) {
    return -1
  }
  buf[pos] = 0
  pos += 1
  return pos
}

Parsing the Response

After we send the packet over UDP, we then have to parse the response we get back. To do this, we first read the ANCOUNT field from the response header, which sits at byte 6, so we set our position there to read it. Once we have that, we skip past the question section, which starts at byte 12. We loop through the domain name until we hit the null terminator, then move on to the answer section. From there, we loop through each answer provided and parse it into our response buffer. Finally, I printed out the results.

Final Thoughts

Overall, it was a cool little project to work on! I haven’t done much with networking, so I feel like this was a great introduction into how the network works and got me some real insight into it. I’m also glad I decided to deep dive into Odin, because it’s such a great language that works so well with how my brain reads code. I know if I wanted to revisit this project, I could take it further, so I might later—but right now I want to look into graphics stuff.

ABX Development

I started a dev log for my game! I’m hoping to post those blogs on the 15th and these programming journey blogs on the 30th—that’s the pattern I’m going for. They’re different RSS feeds, but if you’re signed up on Buttondown, you’ll get alerts for both blogs, since the free tier only allows one email feed.

Anyways, I’m still in the process of rewriting it because I’ve taken some side quests along the way. I forgot how much I’d actually done on the game in C, so there’s a fair bit to rewrite. However, once it’s fully rewritten, I don’t think it’ll take me much longer to finish programming the main parts of the game, because the way I’ve structured my files in the Odin version is way better and lets me easily add and make changes. Part of that is because my knowledge of file structure has gotten better, and I also think Odin has helped a lot with that. I got really tired of juggling .h files, so the fact that all the files can talk to each other in Odin is really nice. So hopefully not too much longer and I will have a demo ready to release 🤞

You Achieved Greatness but At What Cost (…your pinky fingers)

So I’m betraying my Vim roots to try Emacs, but it’s like Vim-flavored Emacs, lol. I recently installed Doom Emacs because I’ve only ever heard memes about Emacs but never actually tried it, so I figured I should give it a fair shot before judging it too harshly. Since I only installed it about 3 days ago, I haven’t gotten too much practice with it yet, but I can definitely see the appeal. The idea of having everything in one place is really appealing to me: coding, Emacs; note-taking, Emacs; calendar, Emacs; music, Emacs; anything to do with text, Emacs. It’s also stupidly fast once you have the daemon launch on startup. So check back in the July wrap-up, and I’ll definitely have a stronger opinion by then!

To Close

Wow, I wrote a lot in this blog, but I kind of liked going into more detail on the project I worked on, so I might keep doing that. A small monthly project alongside my long-running one—that’s an idea! I could go into detail about the monthly one here and briefly mention the long-term one, but give that a dedicated blog of its own. I kind of like that idea! Anyways, thanks for reading—until next time!