I've made the start of a simple telnet server, and am having trouble trying to add MCCP. I've looked at a few different implementations, but seem to be missing something...

My send buffers are c++ strings, they are only converted when being used for send().

I'm using a char buffer (cbuff) to convert the IAC WILL MCCP as such:
sprintf(cbuff, "%c%c%c", 255, 251, 86);
and then cbuff gets added to the c++ string send buffer (like any other message would) before being sent to the client.

Trouble is, the clients I've tested with just seem to send back a "V" with no carriage return.

GMud's negotiation is broken, for example, and it will actually display 'V' (ASCII character 86) if you send it IAC WILL MCCP.

On the other hand, if your client supports MCCP and it's still doing it, it could be that your mud is discarding nonprintable characters. If that's the case, I'm assuming you don't support any other protocols at this stage? You may want to take a look at my protocol snippet.

Have tested with tintin++, Mudlet, and just telnet from terminal. However I meant that the message I receive back from the client just puts V into the in-buffer (waiting for a /n or /r before handling).

Yeah don't support any other protocols yet, basically have a socket server so far. And I am disabling some non printable characters from coming in, sorta assumed the client would send back IAC,DO/DONT,86 and a /r or /n.

Well TinTin++ and Mudlet both support MCCP2, and windows telnet should correctly indicate that it doesn't support it.

However most muds ignore nonprintable characters, so if you send them IAC DO MCCP (or IAC DONT MCCP) they'll strip out the first two bytes and treat the third as regular input. That would result in the behaviour you've described.

What you need to do is check the input for IAC, preferably with a state machine, so that you can read whatever comes after it and react accordingly. You can still strip nonprintable characters from in-band input, but you shouldn't strip them from out-of-band negotiation and subnegotiation sequences.

This is handled by the ProtocolInput() function in my protocol snippet, if you want to see a working example.