return to table of content

The curious case of the missing period

evmar
44 replies
23h23m

A portion of this code implemented a SMTP client.

If I wanted to root cause this, the real problem is right there. Implementing protocols correctly is hard and bugs like in the post are common. A properly implemented SMTP client library, like one you would pull off the shelf, would accept text and encode it properly per the SMTP protocol, regardless of where the periods were in the input. The templating layer shouldn't be worrying about SMTP.

throw10920
17 replies
14h38m

The real problem is that SMTP is a "plain-text" protocol that includes in-band signaling. It literally happened because SMTP defined "a line that only contains a single period in it" as a control sequence and not a literal line that only contains a single period in it.

SMTP is an example of an unnecessarily complex design, and the implementation bugs reflect it. SMTP shouldn't be hard for someone to correctly implement by themselves (even though I agree that people shouldn't be re-inventing the wheel).

pja
12 replies
7h35m

At some point, all protocols include in-band signalling somewhere: You have to put packets on the line, and those packets are ultimately just a stream of anonymous bytes.

If it wasn’t a period, it would be something else & you’d have to handle that instead.

throw10920
4 replies
6h56m

all protocols include in-band signalling somewhere

That's an incredibly reductionistic view of the world that's utterly useless for anything (including actually engineering systems) except pedantry. It's obvious that the level at which you include control information is meaningful and significantly affects the design of the protocol, as we see in the submission. Directly embedding the control information into the message body does not lead to a design that is easy to implement.

If it wasn’t a period, it would be something else & you’d have to handle that instead.

Yes, and there are many other design choices that'd be significantly easier to handle.

TeMPOraL
3 replies
5h43m

No, it's not reductionist and pedantic. It's a reminder that there is no magic. Building an abstraction layer that separates control and data doesn't win you anything if, like the people in the article, you then forget it's a thing and write directly to the level below it.

throw10920
1 replies
5h25m

No, it's not reductionist and pedantic.

It's very reductionistic, because it intentionally ignores meaningful detail, and it's pedantic because it's making a meaningless distinction.

It's a reminder that there is no magic.

This is irrelevant. Nobody is claiming that there's any magic. I'm pointing out the true fact that details about the abstraction layers matter.

In this case, the abstraction layer was poorly-designed.

Good abstraction layer: length prefix, or JSON encoding.

Bad abstraction layer: "the body of the email is mostly plain text, except when there's a line that only contains a single period".

There are very, very few problems to which the latter is a good solution. It is a bad engineering decision, and it also obfuscates the fact that there even is an abstraction layer unless you carefully read the spec.

-------------

In fact, the underlying problem goes deeper than that - the design of SMTP is intrinsically flawed because it's a text-based ad-hoc protocol that has in-band signaling.

There are very few good reasons to use a text-based data interchange format. One of them is to make the format self-documenting, such that people can easily read and write it without consulting the spec.

If the spec is complex enough that you get these ridiculous footguns, then it shouldn't be text-based in the first place. Instead, it should be binary - then you have to either read the spec or use someone else's implementation.

Failing that, use a standardized structured format like XML or JSON.

But there's no excuse for the brain-dead approach that SMTP took. They didn't even use length prefixing,

nottorp
0 replies
4h30m

Why not protobufs inside protobufs then?

pja
0 replies
1h40m

No, it's not reductionist and pedantic. It's a reminder that there is no magic.

Exactly! This is an even better phrasing of my point.

treflop
3 replies
4h3m

From my experience, almost no protocols have in-band signaling. No protocol I’ve ever built has in-band signaling because it’s nuts.

You always know what the next byte means because either you did a prefixed length, your protocol has stringent escaping rules, or you chose an obvious and consistent terminator like null.

pja
1 replies
1h42m

If your metadata about the data is in the same channel as the data then you’re doing in-band signalling.

treflop
0 replies
1h3m

Not in modern times.

The terms harken back from the day of circuit switched networks but now that we have heavily transitioned to packets, bands are an artificial construct on top of packets and applying the term isn’t very clear cut.

The main property of in-band data in the circuit-switched network days is that you could inject commands into your data stream. If we apply that criteria that to a modern protocol, even if you mix metadata and data in the same “band,” if your data can never be interpreted as commands then “out of band” makes an apt description.

See https://en.m.wikipedia.org/wiki/Out-of-band_data

yencabulator
0 replies
3h34m

SMTP has stringent escaping rules. The authors of the code in the article were incompetent.

akvadrako
1 replies
4h55m

That isn't true at all. In most binary protocols, you put the size of the message in the header. Then any sequence of bytes is allowed to follow. There are no escape sequences.

pja
0 replies
1h43m

That’s still in-band signalling: The metadata is in the same channel as the data.

BoppreH
0 replies
5h31m

That's not a very useful definition of "in-band signaling". For me, the main difference is an out-of-band protocol that says:

"The first two bytes represent the string length, in big-endian, followed by that many bytes presenting the string text."

and an in-band signalling protocol:

"The string is ended by a period and a newline."

In the second one, you're indicating the end of the string from within the string. It looks simpler, but that's where accidents happen. Now you have to guarantee that the text never contains that control sequence, and you need an escaping method to represent the control sequence as part of the text.

eviks
1 replies
13h38m

Why didn't they use actual control sequences instead of text chars, ASCII had plenty of those?

inejge
0 replies
13h3m

When your debugger is telnet circa 1980, non-printable characters are a liability. (You pay for it later, when people who've never seen "...end with <CRLF>.<CRLF>" botch the implementation.)

davedx
0 replies
7h46m

Honestly this part with the periods, while unusual, isn’t really complex. The two rules regarding the periods were like a small paragraph of text. I agree with sibling comment from Temporal, this is purely a “skill issue”, not a protocol issue

Animats
0 replies
40m

SMTP is that way because running the entire contents of a message through some escape character processing was expensive back then, in the era of 0.25 MIPS machines.

quotemstr
11 replies
23h15m

Implementing protocols correctly is hard

That's why it's a best practice to specify protocols at a very high level (e.g. using cap'n'proto) instead of expecting every random sleep-deprived SDE2 to correctly implement a network exchange in terms of read() and write().

blueflow
10 replies
22h59m

That why you have to read the specs of the protocol you want to implement. Its a matter of engineering rigorousness. Brute-forcing until "it works" doesn't cut it.

vlovich123
6 replies
20h40m

I think you're missing the fact that experience has taught us repeatedly that separating the protocol definition from the wire format is a good idea. But sure, feel free to ignore the many lessons and blame it on individuals as if anything could be implemented free from human laziness, error, & economic demands (which btw is ironically a lack of engineering rigour which I was always taught as you assume that humans will be lazy, corrupt, make mistakes & that economics of making things as cheap as possible are a real part of engineering & not something to handwave away as "they're not doing real engineering").

robryk
3 replies
19h41m

To steelman the GP's POV: there are other parts of solutions to problems where similar levels of rigour are required and cannot be filled in by using a preexisting library (state machines for distributed business logic come to mind as an example). Eliminating the need for that here doesn't help that much in general, and might even make things worse, because it gives people less experience with tasks demanding rigour before they tackle ones that are both subtler and harder.

vlovich123
2 replies
19h21m

Learning to blindly follow a spec for the purposes of parsing the SMTP wire protocol doesn't give you extra ability to follow the state machine or distributed business logic specs better. It just adds to the overall opportunities for you to make a mistake. This also ignores the fact that SMTP specs is split across multiple RFCs with no single normative version which further complicates the probability that you implement the spec correctly in the first place.

Engineers get better faster because they leverage better tools and build tools to overcome their own shortcomings and leverage their strengths, not by constantly being beat into shape by unforgiving systems.

To be fair, what you and OP said is not an uncommon mentality. It's even shared in a way by Torvalds:

[easier to do development with a debugger] And quite frankly, I don't care. I don't think kernel development should be "easy". I do not condone single-stepping through code to find the bug. I do not think that extra visibility into the system is necessarily a good thing.

Quite frankly, I'd rather weed out the people who don't start being careful early rather than late. That sounds callous, and by God, it _is_ callous. But it's not the kind of "if you can't stand the heat, get out the the kitchen" kind of remark that some people take it for. No, it's something much more deeper: I'd rather not work with people who aren't careful. It's darwinism in software development. It's a cold, callous argument that says that there are two kinds of people, and I'd rather not work with the second kind. Live with it.

He has similar views about unit tests btw.

I personally would prefer to work with people who are smart & understand systems and have machines take care of subtle details rather than needing to always be 100% careful at all times. No one writing an SMTP parser is at Torvald's level.

I'm not arguing that this excuses you from being careful or failing to understand things. I'm saying that defensively covering your flank against common classes of mistakes leads to better software than the alternative.

im3w1l
1 replies
18h8m

This also ignores the fact that SMTP specs is split across multiple RFCs with no single normative version which further complicates the probability that you implement the spec correctly in the first place.

This is a point I agree with and the fact I see it mentioned so rarely, that standards are split across multiple RFC's makes me suspect that people don't mention it because they don't know because they never read them in the first place, and rather try to follow the implementation of some existing program.

256_
0 replies
1h54m

It can get tedious and annoying, but I don't think it actually affects the likelihood that you'll implement something wrong. The RFCs link to each other when needed. Also groups of RFCs often get combined and edited into a single revised RFC for simplicity.

This makes me wonder: How could the IETF's approach to standardisation be improved? I'm not sure how to fix this problem without overhauling everything.

ninkendo
0 replies
15h31m

Yeah someone should get in their Time Machine and tell those idiot SMTP RFC authors in the 1980s that they should have used a wire format that wouldn’t be invented for another 30 years.

blueflow
0 replies
19h12m

Research is also a real part of engineering. One should not omit it and end up with a SMTP implementation like in the article.

Joker_vD
1 replies
21h21m

Reading is for nerds. Real programmers™ write. And they write code, for the machine, not docs or specs or any other silly stuff intended for interhuman communication.

bregma
0 replies
7h54m

Ah, yes, the Agile manifesto.

Gigachad
0 replies
7h1m

You don’t need to understand SMTP to send email. You just use a library that implements it and lets you just pass in a html doc.

TeMPOraL
5 replies
9h35m

The real problem isn't the protocol, but the cowboy approach to interacting with it. It's not hard to "accept text and encode it properly per the SMTP protocol", you just need to realize you need to do it in the first place.

There is a multitude of classes of errors and security vulnerabilities, including "SQL injection", XSS, and similar, that are all caused by the same mistake that this case of missing period was[0]: gluing strings together. For example, with SQL queries, the operation of binding values to a query template should happen in "SQL space", not in untyped string space. "SELECT * FROM foo WHERE foo.bar = " + $userData; is doing the dumb thing and writing directly to SQL's serialized format. In correct code (and correct thinking), "SELECT * FROM..." bit is not a string, it just looks like one. Same with HTML templating[1] - work with the document tree instead of its string representation, and you'll avoid dumb vulnerabilities.

So, if you want to avoid missing dots in your e-mails, don't inject unstructured text into the middle of SMTP pipeline. Respect the abstraction level at which you work.

See also: langsec.

--

[0] - And therefore should be considered as a single class of errors, IMO.

[1] - Templating systems themselves are thus a mistake belonging to this class, too - they're all about gluing string representations together, where the correct way is to work at the level of language/data structures represented by the text.

spankalee
3 replies
4h18m

[1] - Templating systems themselves are thus a mistake belonging to this class

This is not universally true.

JavaScript has an amazing feature called tagged template literals which let you tag a string with interpolations with a function that handles the literal and interpolation parts separately. This lets the tag function handle the literals as trusted developer written HTML or SQL, and the interpolations as untrusted user-provided values.

Lit's HTML template system[1] uses this to basically eliminate XSS (there are some HTML features like "javascript: " attributes that require special handling).

ex:

    html`<h1>Hello, ${name}</h1>`
If `name` is a user-provided string, it can never insert a <script> or <img> tag, etc., because it's escaped.

There are similar tags for SQL, GraphQL, etc. Java added a similar String Templates feature in 21.

[1]: https://lit.dev/docs/templates/overview/

cratermoon
2 replies
3h46m

If `name` is a user-provided string, it can never insert a <script> or <img> tag, etc., because it's escaped.

Be careful with that "never". A curious and persistent person might discover a bug in the implementation, leading to something like the Log4Shell issue.

VBprogrammer
1 replies
3h9m

Not sure why you are being downvoted here. It's a fair point and properly escaping your data is only one part of the overall security picture but you should also be strictly validating data at the inputs to your system too.

spankalee
0 replies
2h40m

Luckily, for Lit specifically, the "escaping" is done by the browser by setting textContent, so the string literally never passes through the HTML parser. Any string is valid text content, and if you found a bug that permitted unsafe text to be parsed as HTML somehow, it would be a browser bug and a very, very serious one.

But it'd be similar with with other template systems. If the interpolation should allow any string, there's really no validation to be done.

mananaysiempre
0 replies
5h30m

For what it’s worth, some markup-first template systems have tried to respect the target format’s structure—Genshi[1] and TAL[2] come to mind, and of course XSLT (see also SLAX[3]). I said “markup-first” so that the whole question isn’t trivialized by JSX.

[1] https://genshi.edgewall.org/wiki/Documentation/xml-templates...

[2] https://zope.readthedocs.io/en/latest/zopebook/AppendixC.htm...

[3] https://juniper.github.io/libslax/slax-manual.html

xg15
4 replies
11h30m

Sounds like a great idea, until you find that your SMTP library pulls in 5 other libraries as its own dependencies and those each pull in 3 transitive dependencies of their own, one being some kitchen sink/toolbox project where only 1% of its code is actually relevant to the dependant and the rest is dead weight - but which pulls in 20 more dependencies for functions that are literally never called in your project - and before you know it, your codebase bloats up by several MB and you get CVE warnings for libraries that you didn't even know existed, let alone that you're using them.

Gigachad
2 replies
7h2m

This doesn’t seem like a real issue. Servers can handle a several megabyte executable, and CVE warnings for libraries you aren’t using can be ignored.

markisus
1 replies
5h55m

In the hypothetical above, you won’t have any way to know which libraries are actually being used unless you read through the source code. Many libraries will transitively include protobuf, but most functions will not call protobuf.

bronson
0 replies
1h37m

Agreed. Even if you establish that it's not being used today, that doesn't mean that it will continue to be unused after the next few commits land.

And, even though you might not see a way to call into the unused code, an attacker might find a way (XZ Utils).

andruby
0 replies
4h30m

I agree with this principle in general.

But for SMTP libraries, that's often part of stdlib (Ruby, Python, PHP, ...).

the_real_tjaart
0 replies
23h15m

I agree 100%.

om8
0 replies
18h11m

Single responsibility principle in its finest.

aftoprokrustes
13 replies
20h8m

I will not comment on the technical part, as others already did it better than I could, but it just reminded me of an anecdote that reminds of the importance of such trivial things as a period at the end of a sentence:

In Germany, where I work, it is usual at the end of employement to ask for a letter of recommendation ("Zeugnis") that lists the tasks performed, and how good the employee was. It is an important document, as it will typcally be required when applying for jobs. Obviously, no employee would accept a document explicitly stating "this guy is a lazy bastard, do not hire him", so there is a "Zeugnissprache", a "secret code" to disguise this information as praise. One part of this code is that a missing period in the last sentence means "please ignore everything said here, this guy is horrible".

How do I know? I let a lawyer check my Zeugnis after my last employment, and (I assume out of lack of care, as all my performance reviews were positive) the last sentence was missing the period.

Biganon
9 replies
18h57m

Secret codes being used in recommandation letters are an urban legend. HR people have no incentive to create a secret code for them and their potential rivals, let alone teach it to new HR people while also keeping it secret.

This legend comes from the fact that HR people cannot be too explicit about the fact that you've been a pain in the ass (you could probably sue if it's too transparent), so if they have nothing positive to say they will commend your punctuality or something equally as mundane. It's not secret codes, it's like... "bless their heart", but in HR talk. Plausible deniability if you want to sue, I guess. "But it's a good thing, your honor! They were always on time!"

boxed
8 replies
18h53m

So.. secret codes are a myth, and here are some examples of secret codes?

Biganon
6 replies
18h32m

Please refrain from willingly picking the naive interpretation when you've understood my point perfectly fine, it's against the rules of this website.

...sigh:

Secret codes as in "watermark-level omission of characters" are a myth. Lingo and jargon do however exist, and convey meaning in a particularly subtle way. They are shared and taught by culture, not by a secret handbook passed down from generation to generation. See also dogwhistling.

The goal is to protect the issuer, not to selflessly inform the recipient.

HankB99
3 replies
15h55m

This reminds me of the joke whose punch line is

You will be lucky to have this person work for you.
angst_ridden
2 replies
4h29m

"I cannot recommend X too highly. X always served as an example to their colleagues. The quality of X's code was unequalled in our department, and X's work always merited special attention." (etc)

doktrin
1 replies
3h51m

I cannot recommend X too highly

This isn't a veiled statement. It's outright dunking on the applicant.

thedanbob
0 replies
3h32m

It can be interpreted both ways: "I cannot recommend X too highly (because they suck)" vs "I cannot recommend X too highly (because whatever praise I give will be inadequate)"

keybored
0 replies
8h26m

May HR personnel live in interesting times.

KwanEsq
0 replies
1h40m

"Damning with faint praise" is hardly a secret code.

mwigdahl
1 replies
19h51m

Did you check your performance reviews to see if they were missing periods also?

pests
0 replies
19h9m

Why would they need to keep it secret if its a review and internal-only?

lisper
0 replies
19h35m

One part of this code is that a missing period in the last sentence means "please ignore everything said here, this guy is horrible".

Gee, what could possibly go wrong?

kazinator
12 replies
22h3m

Why would a cron job that sends e-mails need to implement its own SMTP client???

You just use the mail program from mailutils or whatever.

Just from a point of view of deliverability, developing bare bones SMTP interaction over a socket is a nonstarter. You can't just connect to random mail exchange hosts directly and send mail these days. A solution has to be capable of connecting to a specific SMTP forwarding host (e.g. provided by your ISP). For that, you need to implement connections over TLS, with authentication and all.

Also, a slightly ironic thing is that cron already knows how to send mail. The output of a cron job is mailed to the owner. Some crons let that mail address be overriden with a MAILTO variable in the crontab or some such thing.

ummonk
4 replies
21h6m

Just another example of Zawinski's law, no?

onlyrealcuzzo
3 replies
20h30m

Zawinski's Law captures common market pressure on software solutions, stating that “every program attempts to expand until it can read mail. Those programs which cannot so expand are replaced by ones which can.”
sjf
2 replies
19h45m

The modern version would seem to be every platform expands until it includes instant messaging.

tantalor
0 replies
19h41m

IM is just fancy email.

easyThrowaway
0 replies
4h55m

I believe that's outdated already, the next...uh, expansion layer would be some sort of generative AI features.

In other words, every other platform expands until it can summarize emails.

partdavid
3 replies
19h48m

There's actually a lot of things that embed a SMTP client for sending mail that should use a host MTA. The reason is that the user who actually wants to use "the thing" is often just about able to enter an SMTP server, but there's no way "the thing" can trust that a properly-configured sender MTA like sendmail is configured. Companies have huge fleets of servers that can't send system mail, and it's often not really under the control of the author of your frobulator program or the user, either. Mail itself is a specialist configuration, often requiring prerequisites in DNS, crypto stuff, policies, etc. It's "too much" for just being able to have your wallet or whatever send you mail; "contact your system administrator" is just often not a realistic option, in both large and small scales.

It's not a good reason, no--definitely not--but it's a real reason.

leni536
1 replies
5h38m

Use an SMTP library then.

partdavid
0 replies
2h10m

Agreed, for sure.

kazinator
0 replies
14h12m

The SMTP client needs to be configured. E.g. if there is a particular SMTP forwarding host that must be used, you have somehow get the "mail" utility to use that, and you must somehow any get any given SMTP library to also use it.

bastawhiz
2 replies
17h1m

There's no universe where I'd trust that there's already some binary on the system to send mail for me, but I definitely wouldn't roll my own. For any reasonable language, there's a multitude of SMTP client libraries available if there's not already one in the stdlib.

kazinator
0 replies
14h15m

They are relying on a cron program being there, so ...

You can require it and it gets provisioned.

erhaetherth
0 replies
14h22m

I'd give up instantly and outsource it to a SaaS. I'm no expert on email but I know enough to know it's a PITA and managing delivery is a thing, and you have to make sure your DNS is configured right out the receiver will just reject it, there's reputations to consider, you probably want to throttle how fast you send it.. ugh.

jiveturkey
11 replies
23h15m

This meant some customers received emails informing them their new premium was now $2700 instead of $27.00.

there's a secondary issue here, why in the world would you auto split a monetary value across a numeric decimal indicator? why would you split lines at all for this use case?

kr0bat
4 replies
22h46m

As mentioned, the SMTP protocol only allows for 1000 bytes of data per line. The author also mentions that they are sending html emails, which ignore line breaks.

So a message intended to be sent by an SMTP client:

DATA

Hello customer,<br>[978 characters] 27.00

Was erroneously formated into:

DATA

Hello customer,<br>[978 characters] 27

.00

.

The period after 27 will be removed. And this is how the html will be rendered.

Hello customer,

[Lots of text] 2700

f33d5173
2 replies
22h37m

Hmmmm, html doesn't ignore line breaks, it just treats them as any other whitespace, where a consecutive sequence is folded into a single space. 27 00 would still be quite confusing, of course

tingletech
1 replies
22h17m

I think the space character in the comment above is representing a new line on the wire.

kr0bat
0 replies
21h57m

Yep, I didn't add enough newlines. Fixed

jiveturkey
0 replies
14h20m

but html does not ignore line breaks. when part of body text, a run of whitespace (including newline) becomes a single whitespace when rendered.

so splitting 27.00 on the . becomes 27 00, because the CRLF is significant to the client.

you would want to split at whitespace, not at any other character -- unless you had a 999+ string of non-whitespace of course.

perhaps the author didn't know or didn't realize or thought it insignificant to his point that in addition there was a quoted-printable encoding, in which case i believe the trailing/mandatory CRLF can be made non significant for client rendering. personally i still would have split on actual whitespace. (well, i wouldn't have written an smtp client in the first place.)

pwg
1 replies
22h52m

From TFA it is stated that they were doing the split of lines because of the "1000 octet" maximum line length requirement of the SMTP protocol.

And, they also state that the period disappeared because it was placed at the start of the next line when the split occurred.

From which one can deduce that they were doing the most basic "split" possible, splitting at the exact 1000 octet point, i.e. something like:

   if (length(line)>1000) then:
     line1=string_range(line,0,999)
     line2=string_range(line,1000,end)
   fi
And if the period in 27.00 ended up exactly at offset 1000 in "line" then it got 'split' into line 2 as the first character of line2.

Aloisius
0 replies
22h41m

It would split at 998 characters, since each line must end with CRLF.

HeatrayEnjoyer
1 replies
22h58m

I do not understand what you are asking. "$27.00" is a standard expense format.

hunter2_
0 replies
22h31m

I think GP was was using the phrase "auto split ... across [character]" in reference to characters that can cause line breaks for "word wrap" purposes in page layouts. For example, a normal space is a character that causes line breaks, but a non-breaking space (nbsp) is not. A hyphen, a tab, a zero-width space (zwsp), and several other characters are also generally used for line breaking. I think GP is saying that the decimal indicator -- the fourth character of "$27.00" -- should not be used for breaking. I think GP assumes that the problematic line breaking in TFA is akin to the type of "word wrap" page layout logic I've just explained; in reality the line breaking in TFA has nothing to do with that, it's simply breaking at 1000 octets (probably for reasons of buffer size, certainly not page layout) regardless of what character is in that position, so this whole thing is moot. GP needs to RTFA!

kccqzy
0 replies
22h59m

From the article:

The maximum total length of a text line including the <CRLF> is 1000
Aloisius
0 replies
22h53m

If the period was the 999th character in the line, it would split it to the next line since the maximum line length in SMTP is 1000 characters including CRLF.

teddyh
10 replies
22h47m

I see two huge bad habits here. The first is the obvious one, as pointed out by many commenters here: Don’t implement standards haphazardly, if you even should do so yourself. Either give the implementations the necessary care and attention, or use a pre-made library.

But the other thing is: Don’t vendor your dependencies. Those libraries you use need to be updated regularly and timely, and absolutely not “only as necessary”. If updates lag behind or are avoided entirely, bugs like this can be huge problems even when the upstream code has been fixed, for people who thought that they should update only when they, themselves, see a problem or need.

the_real_tjaart
5 replies
22h45m

Don’t implement standards haphazardly, if you even should do so yourself. Either give the implementations the necessary care and attention, or use a pre-made library.

I agree 100%.

ryandrake
4 replies
21h6m

Famous last words at >50% of the companies I've worked: "Just implement as much of standard X as you need to ship an MVP of feature Y!"

rrr_oh_man
3 replies
20h32m

Can you share a story?

ryandrake
1 replies
20h2m

As the sibling comment already mentioned, file format reading is a big one. Oh, a customer needs us to ingest CSV data. Let's just roll our own CSV parser, the file format is simple, right? What could go wrong? Same for graphics formats. We need to read PNG files, so let's just figure out the format and implement just enough to read the handful of files we have on hand. Even though libpng exists.

Another, more domain specific one: We have to talk to a GPS receiver that connects via RS-232 and outputs NMEA formatted data. This device, like all such devices, outputs a small subset of the standard. So parse just enough of it for that one device we have to get working, and ship it. Then someone attaches a different device that outputs a different subset of the standard and the software fails (sometimes gracefully, sometimes not).

kruador
0 replies
7h1m

That reminds me of the Windows Mobile 6.5 device with built-in GPS receiver, where on the 31st day of the month, the API would actually output the date as the 0th day of the following month. This wouldn't have been a problem, except that we were using that to set the system clock. The devices would reset their clock if allowed to go completely flat, but they didn't have reliable NTP support either. I believe the OS would just ignore our attempt to set the date to an invalid value, leaving it at the system default date.

It was a struggle to get the vendor to even acknowledge the problem, since it would only happen on 7 days out of the year, and usually 60 days apart. (There are only two consecutive months with 31 days, July and August.) It did eventually get fixed.

I seem to recall writing a workaround, but then it being stalled by our customer's Change Control Board.

maxbond
0 replies
20h13m

I had a very difficult to track down bug that ended up losing us a very big fish client, that came down to parsing a binary file with an ad-hoc parser that looked for the index of a header. They didn't realize that after the header was some metadata, so a small amount of metadata was interpreted as data. I fixed it by writing a proper ad-hoc parser that actually worked on a header-by-header level. But the damage was done and we had looked like buffoons to the client.

Was very hard for the team. There was a shouting match over it, some hard feelings. The code was written in the spirit GP alludes to by an enthusiastic executive who wanted to help lighten the load. I should've rejected the PR but was intimidated to reject the exec's code (not an engineering reason for an engineering decision!). The exec was a good data scientist but not as strong a coder as me, and parsing binary files is one of my specialties.

Friends don't let friends parse using "indexOf()".

otterley
3 replies
22h36m

Don’t vendor your dependencies.

The alternative seems worse: your own application's stability is now at risk against upstream changes that could break your code. Sure, you might not get a fix immediately, but I'd rather know I'm making a change because I need a fix than introducing instability and additional risk that I don't want to subject myself to. "If it ain't broke, don't fix it."

skybrian
1 replies
22h15m

I like Go's approach, which ensures that all upgrades happen when you choose to do some upgrades.

(For apps, a lock file will do it too.)

teddyh
0 replies
2h22m

You can choose to either live at the slightly-bleeding edge (as determined by “stable” releases, etc), or to live on the edge of end-of-life, as discussed here: <https://news.ycombinator.com/item?id=21785399>

(And surely you should have tests to verify all your own functionality after upgrading a dependency?)

jeffbee
5 replies
22h50m

If a person has never heard of dot stuffing they're never going to believe what other horrors lie within the email space. Header folding, quoting in the local part, ipv6 literals, etc.

blueflow
4 replies
22h40m

Whats the issue with ipv6 literals?

jeffbee
3 replies
22h22m

Everything that is normally annoying with IPv6 literals, plus the fact that it's encapsulated as [IPV6:<the address>] and the address could take a dumb form such as ::1.2.3.4. But I mentioned it because it might initially seem to a neophyte in this field that the thing to the right of the rightmost @ sign is a name you can pass to your resolver. It might not be.

kazinator
1 replies
22h19m

The IPv6 notation claims : for separating digits. But that's already an established notation for port numbers: 10.1.2.3:456. Oops!

The square brackets allow us to stick a port number on it: [ffff::0123:4567]:6301

jeffbee
0 replies
22h3m

Yes, but the RFCs permit 6v4 addresses such as ff:ee::aa:1.2.3.4

jcranmer
0 replies
22h1m

Note that IPv4 address literals are encoded in square brackets (e.g., user@[127.0.0.1]), so it's really all IP address literals that could be a problem rather than specifically IPv6 literals.

blueflow
5 replies
23h33m

You can avoid this mess altogether by using the quoted-printable content encoding when generating emails.

unilynx
4 replies
22h50m

How so? Quoted printable doesn't require the dot to be encoded.

Maybe you're thinking of base64 encoding?

blueflow
3 replies
22h41m

Doesn't require, but comfortably allows you to do.

unilynx
2 replies
21h40m

Agreed.

But this is SMTP, I have no doubt there are gateways out there that will reencode the mail and put the dot back in the wrong place, eg in the name of wrapping all URLs behind a phishing-warning-page

opello
1 replies
20h48m

I think the point here is that any subsequent implementation handling the message may well not be susceptible to the problem thus avoiding the erroneous behavior.

int_19h
0 replies
13h10m

It can handle it fine in that sense, but still unquote it before sending it over to the next node. Which might have the bug.

kazinator
4 replies
22h8m

But the line "We are happy to welcome you to our family." is not anywhere near the line limit. There is something else going on here, like perhaps the whole thing actually being an HTML MIME attachment, perhaps? IN which it is like

  ... lots of text ... <br>We are happy to welcome you to our family.<br>
or whatever. But if you blindly split HTML into lines, it will break tags.

canucker2016
1 replies
20h51m

and probably the vast majority of the people receiving those broken HTML emails will realize there's something wrong with the email's formatting, chalk up the problem to a company that can't be bothered to correctly write a legible email and lower the company's competence rating, and go on to the next email.

the company is just blindly unaware to their minor problem.

dylan604
0 replies
20h43m

rarely do I hold the sins of an incompetent marketing department against the company itself. otherwise, there'd be no company left deemed as competent because all marketing departments are incompetent.

the_real_tjaart
0 replies
17h54m

This was just an example, sorry for not providing a real example that had the exact character count.

Aloisius
0 replies
19h56m

Quoted-printable encoding with soft breaks would allow blind splitting of lines without breaking HTML tags.

While quoted-printable is supposed to have a max line limit of 78 characters including CRLF rather than 1000, email clients tend to be permissive.

davidmurdoch
4 replies
22h8m

Loved reading this. This is now one of my new favorite bug hunt stories!

the_real_tjaart
0 replies
5h51m

Thanks for sharing!

davidmurdoch
0 replies
20h27m

That's my favorite.

the_real_tjaart
0 replies
17h51m

I am glad you enjoyed it.

userbinator
2 replies
13h24m

I suspect a lot of people are no longer being taught these fundamental protocols by manual interaction with a terminal, since that's what SMTP seems to have been originally intended for; and as someone who actually made use of that for a nontrivial amount of time, the "single dot on a line" to end a message has been permanently etched into my memory.

Relatedly, escaping somehow seems to be a foreign concept for a lot of programmers, who wouldn't ever see the above situation and ask themselves "but what if I want to send an email with a line containing a single dot?" yet another large group of them finds it perfectly logical and easy to understand.

edanm
1 replies
9h43m

I suspect a lot of people are no longer being taught these fundamental protocols by manual interaction with a terminal,

I'd be surprised if any significant portion of software developers learned anything like that in the last 30 years, at least.

Maxion
0 replies
6h10m

I mean, if we did that for everything in webdev, It'd take 30 years of training before you'd get to do your first PR!

marcosdumay
2 replies
23h29m

So... SMTP client misses basic SMTP functionality that fits in the 20 lines summary.

Good luck having it handle any of the SMTP craziness that isn't on the short introduction to the protocol.

banish-m4
1 replies
23h20m

When you roll your own library duplicating something that likely already exists, you own it all. And that doesn't include all of the vendor-specific nonconforming edge-cases.

marcosdumay
0 replies
15h8m

I guess when you roll your own library, you get an obligation to read the introduction of the 10 pages long standard.

Or the usage example.

m3kw9
2 replies
22h31m

For a second I thought this was the health forum

wkat4242
0 replies
20h29m

Yeah I was kinda expecting a picture of a pregnancy test :)

mnw21cam
0 replies
21h38m

Mmm. The story is about a missing full stop.

linsomniac
2 replies
18h26m

Show of hands: Who knew exactly where this was going as soon as "SMTP" was mentioned?

layer8
0 replies
17h36m

As soon as "missing period" was mentioned I suspected it would be about SMTP.

bruce343434
2 replies
18h29m

As someone who configures email servers for a living (among other things): email needs to be replaced, frankly. This is a stupid protocol with even stupider file formats. What is the reason for such a hard coded line limit? It's just a stream of bytes...

Not to mention all the weird bandaids on top of bandaids to try to get sender verification and tamper proof emails working. That alongside the complete lack of end to end encryption.

It's just an incredibly unpleasant tech stack from top to bottom, through and through. The amount of moving parts/pieces of running software needing to cooperate just right to even function as a simple outgoing-only mail server is too damn high.

vardump
1 replies
9h5m

You’re not wrong, but how would you suggest to accomplish this? Replacing whole email infrastructure seems nearly impossible.

bruce343434
0 replies
4h21m

My hope is that people move away from email as they get tired by it, and towards more convenient instant messaging platforms which pretty much get the feature set right (attachments, encryption, blocking/spam provisions, provenance, "stories" and special group chat modes for broadcasting) and for which open protocols arise ((are being forced by the eu)): https://www.theverge.com/2024/2/6/24063705/whatsapp-interope...

In the future, I hope that email will be like fax, or at least treated like http in comparison to instant messaging (https).

Learner100
2 replies
13h0m

- The SMTP client they implemented could insert a newline such that a line was comprised of only a single period.

- The SMTP client spec says that an additional period would be added here.

- The SMTP server spec says that it would remove this additional period, bringing us back to one period.

I don’t get how this led to there being no period at all. Am I missing something?

roer
1 replies
12h57m

The spec says that an additional period should be added on the client, but their implementation did not do that.

Learner100
0 replies
3h30m

Ah, thanks!

talkingtab
1 replies
5h1m

This reminds me of an experience debugging a network protocol implementation - specifically AppleTalk NBP for other ancient people. I had coded everything but my packets were rejected (aka silently dropped) when real (aka Apple implementation) packets were not. I had a copy of the good and bad packets on the screen of my computer and had gone over them byte by byte to find the problem. And there was none. From start to finish they were exactly the same, with correct check sums etc. It was time to go home and I decided just to print the stupid things to look at later.

As soon as I printed them, the error was clear. My version ran to two pages and the good implementation one page. I had not been careful to clear the buffer before sending the data (mbufs don't you know).

This still cracks me up.

the_real_tjaart
0 replies
4h50m

Thanks for sharing!

sethammons
1 replies
17h47m

I recognized the problem on sight; we solved this exact issue but since it was MTA software we knew about periods being special. Unfortunately, people are often solving problems that many others have solved. Maybe AI will allow the lines to be connected or solve for known edge cases like a dot in the smtp data

genewitch
0 replies
16h30m

you know, now that you mention this, i think i have heard of this exact problem before, perhaps from the late 90s; the "full stop on a line by itself" itched a bit, probably for that reason as well.

layer8
1 replies
11h5m

SMTP = Sporadically Missing Trailing Period

;-)

256_
0 replies
1h37m

/SMTP/SMLP/ SMLP = Sporadically Missing Leading Period

(-;

croes
1 replies
11h9m

the first character is a period and there are other characters on the line, the first character is deleted.

Why is it implemented that way?

If a single period means end of mail then more than a period means it's mail data.

Why deleting the period in the first place? Couldn't they store one byte to check the next?

wruza
0 replies
10h18m

Cause you have to send a line with a single period somehow.

  .     Ends body
  ..    One period <--
  ...   Two periods
  .A    A
  ..A   .A
  A     A
Anyway that’s stupid and only helps if you compose your email right in a tcp session.

carimura
1 replies
18h24m

Good story. Reading this reminds me of all the "curious cases of....." that I've solved (or in some cases not solved) over my career and how that feeling of triumph is so deeply tied to why I got into computers in the first place. The pure joy of unraveling the mysteries of engineering... like forgetting a semi-colon somewhere in a 50k LOC Perl backend.

the_real_tjaart
0 replies
17h54m

how that feeling of triumph is so deeply tied to why I got into computers in the first place

I share your sentiment, thank you for reading.

bufordtwain
1 replies
19h12m

What about the missing comma at the end of the first line? :)

the_real_tjaart
0 replies
17h52m

Whoops, :)

xmjw
0 replies
21h6m

Holy shit. This is one of the 2 bugs in my career I never solved. (That I knew about, and lost sleep over, etc…)

tuck1s
0 replies
20h51m

The need for dot-stuffing is a side-effect of email's use of the period as part of the termination sequence CR LF . CR LF. Mishandling of that sequence has led to other recently discussed bugs. https://smtpsmuggling.com/

renewiltord
0 replies
4h19m

Suspected it was the 990 char limit per line but this is another one. I assume this is an old system? Despite many claims about “best practices”, there were definitely past platforms where these didn’t exist and a minimal implementation was safer just because you knew the scope of error.

Of course if it was modern, different question.

readyman
0 replies
18h49m

The title sounds like a pregnancy scare

readthenotes1
0 replies
20h14m

All this about a missing period and nothing about the consistently missing commas. In fact I would say leaving off punctuation on one line paragraphs would be more consistent.

pyuser583
0 replies
43m

For female readers, the title might have a very different meaning.

nytesky
0 replies
22h39m

Wasn’t this an episode of Silicon Valley?

jcpham2
0 replies
6h58m

As soon as I started reading one of the first things that came to mind was the termination period in the smtp server spec. If you’ve ever had to troubleshoot an SMTP transport issue typing SMTP con Ds by hand is a familiar thing. Cool read.

jayceedenton
0 replies
22h1m

A portion of this code implemented a SMTP client.

What the...

irrational
0 replies
20h5m

Seeing that the SMTP client code was borrowed from a previous project we thought it good to let our other teams know about this bug in case they needed to patch it as well. They thanked us and we called it a day.

As soon as I read the above, I knew the below would be the result.

It seems one of our other teams haven't gotten around to patching this bug in their code.
ijuelz
0 replies
1h10m

The night is dark and full of errors.

eschneider
0 replies
21h33m

I guessed the cause of the problem (well, "leading period") from the description, but that's because I've experienced a lot of pain in my life...

edweis
0 replies
11h13m

I like the simplicity of the email membership price increase.

davidwritesbugs
0 replies
11h45m

As I'm implementing an NNTP server based on RFC specs I knew instantly what was happening here without RFA. Dot stuffing, yea 80s protocols baby.

camel_gopher
0 replies
16h40m

I have two kids. The case is not curious at all.

bhaney
0 replies
20h15m

This made me realize I have the opposite problem. Now I have to go update the toy SMTP server that I ended up implementing in a perl script so it handles SMTP clients double-dotting a line.

EVa5I7bHFq9mnYK
0 replies
12h32m

Given that the words Night and Club appear in the picture, the title looked mildly intriguing ..

867-5309
0 replies
7h21m

Every time an employee of our client needed to send out a document via email or needed to print a document that needed to be sent out by the postal services to the customer the employee would have to replace all the placeholders within the document

ironically titled..

256_
0 replies
2h8m

Sometimes I feel like I'm wasting my time by obsessively reading RFCs and specifications for the things I use. This made me feel better about that. And also much more smug.

Other than the obvious moral that protocols should be implemented properly, the moral of the story is that all abstractions are leaky, and it will always be useful to understand the lower levels.