When can a bad String Compare work? String Pooling VS6.0 - visual-studio-2013

Ran into a problem in VS2013, and was asking HOW COULD THIS HAVE EVER WORKED?
A customer had the following macro in production.
(specifics have been changed)
#define IS_NONE( charPtr ) ( ( charPtr == "none" || charPtr == "N/A" ) ? TRUE : FALSE )
My problem was the code was crashing when a block named "none" was requested. The code should have identified a "none" block and skipped requesting the block. But it failed to do so?
I knew the customer code was running production in VS6.0, but was only crashing in VS2013.
The Answer is below.

The test case below builds in Visual Studio 6.0.
The default Visual Studio6.0 behavior is "Program Database with Edit and Continue". Turns out this enables STRING POOLING. So the compiler sweeps through and optimizes ALL compile time strings into a pool, and duplicate strings are ELIMINATED. So all literal definitions of "none" and "N/A" below will POINT TO THE SAME PHYSICAL ADDRESS. As a result the string compare appears to work.
Any dynamically created strings will fail to match, as their addresses will always be unique.
To get this test case to FAIL build it with "Program Database" in VS 6.0 (See screenshot below).
//
// Slight changes to my program from what I compiled
#include "stdio.h"
#define IS_NONE( a_key ) ( ( a_key == "none" || a_key == "N/A" ) ? TRUE : FALSE )
HOST_BEGIN_BLOCK( TestCase ) {
char *psNameNone = "none";
char *psNameNA = "N/A";
char *psNameCAT = "CAT";
if (IS_NONE(psNameNone) ) {
printf("psNameNone Matches NONE\n");
printf("%s psNameNoneAddr 0x%x \"none\" addr 0x%X\n",
psNameNone,psNameNone,
"none");
} else {
printf("psNameNone Does Not Match None\n");
printf("%s psNameNoneAddr 0x%x \"none\" addr 0x%X\n",
psNameNone,psNameNone,
"none");
}
if (IS_NONE(psNameNA) ) {
printf("psNameNA Matches N/A\n");
printf("%s psNameNA 0x%x \"N/A\" addr 0x%X\n",
psNameNA,psNameNA,
"N/A");
} else {
printf("psNameNone Does Not Match N/A\n");
printf("%s psNameNA 0x%x \"N/A\" addr 0x%X\n",
psNameNA,psNameNA,
"N/A");
}
if (IS_NONE(psNameCAT)) {
printf("psNameNA Matches CAT\n");
printf("%s psNameNA 0x%x \"CAT\" addr 0x%X\n",
psNameNone,psNameNone,
"CAT");
} else {
printf("psNameNA does not match CAT\n");
printf("%s psNameNA 0x%x \"CAT\" addr 0x%X\n",
psNameNone,psNameNone,
"CAT");
}
}
Below is an image of the test case passing. Note that all the string ADDRESSES are the same.
Built with "Program Database With Edit and Continue"
This is the test case failing, as one would expect for an incorrect Compare syntax.Built with "Program Database"
Hopefully this will help others trying to figure out why VS6.0->Modern compiler ports appear to work on VS6.0 (1998 compiler) but fail with a modern compiler.

Related

winsock2: How to get the ipv4/ipv6 address of a connected client after server side code calls `accept()`

There are other similar questions on this site, but they either do not related to winsock2 or they are suitable only for use with ipv4 address spaces. The default compiler for Visual Studio 2019 produces an error when the ntoa function is used, hence an ipv4 and ipv6 solution is required.
I did once produce the code to do this for a Linux system however I am currently at work and do not have access to that. It may or may not be "copy and paste"-able into a windows environment with winsock2. (Edit: I will of course add that code later this evening, but of course it might not be useful.)
The following contains an example, however this is an example for client side code, not server side code.
https://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedInternet3c.html
Here, the getaddrinfo() function is used to obtain a structure containing matching ipv4 and ipv6 addresses. To obtain this information there is some interaction with DNS, which is not required in this case.
I have some server code which calls accept() (after bind and listen) to accept a client connection. I want to be able to print the client ip address and port to stdout.
The most closely related question on this site is here. However the answer uses ntoa and is only ipv4 compatible.
What I have so far:
So far I have something sketched out like this:
SOCKET acceptSocket = INVALID_SOCKET;
SOCKADDR_IN addr; // both of these are NOT like standard unix sockets
// I don't know how they differ and if they can be used with standard
// unix like function calls (eg: inet_ntop)
int addrlen = sizeof addr;
acceptSocket = accept(listenSocket, (SOCKADDR*)&addr, &addrlen);
if(acceptSocket == INVALID_SOCKET)
{
// some stuff
}
else
{
const std::size_t addrbuflen = INET6_ADDRSRTLEN;
char addrbuf[addrbuflen] = '\0'
inet_ntop(AF_INET, (void*)addr.sin_addr, (PSTR)addrbuf, addrbuflen);
// above line does not compile and mixes unix style function calls
// with winsock2 structures
std::cout << addrbuf << ':' << addr.sin_port << std::endl;
}
getpeername()
int ret = getpeername(acceptSocket, addrbuf, &addrbuflen);
// addrbuf cannot convert from char[65] to sockaddr*
if(ret == ???)
{
// TODO
}
You need to access the SOCKADDR. This is effectively a discriminated union. The first field tells you whether its an IPv4 (==AF_INET) or IPv6 (==AF_INET6) address. Depending on that you cast the addr pointer to be either struct sockaddr_in* or struct sockaddr_in6*, and then read off the IP address from the relevant field.
C++ code snippet in vs2019:
char* CPortListener::get_ip_str(struct sockaddr* sa, char* s, size_t maxlen)
{
switch (sa->sa_family) {
case AF_INET:
inet_ntop(AF_INET, &(((struct sockaddr_in*)sa)->sin_addr),
s, maxlen);
break;
case AF_INET6:
inet_ntop(AF_INET6, &(((struct sockaddr_in6*)sa)->sin6_addr),
s, maxlen);
break;
default:
strncpy(s, "Unknown AF", maxlen);
return NULL;
}
return s;
}
Example:
{
...
char s[INET6_ADDRSTRLEN];
sockaddr_storage ca;
socklen_t al = sizeof(ca);
SOCKET recv = accept(sd, (sockaddr*)&ca, &al);
pObj->m_ip = get_ip_str(((sockaddr*)&ca),s,sizeof(s));
}

ssh "packet_write_wait: Connection to x.x.x.x port 22: Broken pipe" -- where is the source code?

We got a client ssh to a remote server showing this error. It has always been running fine, no firewall rules change either. When an ssh session is idled over the weekend, it is still connected. Just some times when we 'less' and shift-F on a file for couple of hours, it shows this error.
I'm not trying to solve this problem in this post. We want to look at the ssh source code to figure out what is going on. On Centos 7, I downloaded openssh-7.4p1-21.el7.src.rpm, and extracted openssh-7.4p1.tar.gz. 'grep' through source code and found 'packet_write_wait' function. But curiously, "Broken pipe" (or -i on each word separately) is not found in all the .h and .c files. Where is that error text coming from?
You can find a copy of the OpenSSH source code in github. The packet_write_wait function is in opacket.c:
void
packet_write_wait(void)
{
int r;
if ((r = ssh_packet_write_wait(active_state)) != 0)
sshpkt_fatal(active_state, __func__, r);
}
It calls another function to write the packet. If that fails, it calls sshpkt_fatal. sshpkt_fatal is in packet.c, and its job is to print an error message and then exit.
/*
* Pretty-print connection-terminating errors and exit.
*/
void
sshpkt_fatal(struct ssh *ssh, const char *tag, int r)
{
switch (r) {
case SSH_ERR_CONN_CLOSED:
logdie("Connection closed by %.200s port %d",
ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
[...code removed...]
/* FALLTHROUGH */
default:
logdie("%s%sConnection %s %.200s port %d: %s",
tag != NULL ? tag : "", tag != NULL ? ": " : "",
ssh->state->server_side ? "from" : "to",
ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), ssh_err(r));
}
}
The message that you're asking about is handled by the default case. The last argument, which provides the text after the colon, is provided by calling ssh_err:
const char *
ssh_err(int n)
{
switch (n) {
case SSH_ERR_SUCCESS:
return "success";
case SSH_ERR_INTERNAL_ERROR:
return "unexpected internal error";
[...etc...]
The ssh_err case that you're interested in is this one:
case SSH_ERR_SYSTEM_ERROR:
return strerror(errno);
In short, the "Broken pipe" message comes from the standard library function strerror, which converts error numbers to standard error messages.
The list of standard error codes indicates that "Broken pipe" is associated with the EPIPE error.

Bug in PF_ROUTE on macOS?

I have a question about using PF_ROUTE on macOS to detect IP address changes. Basically, it seems to me that it is broken for IPv4. I have put together a sample program that simply creates the PF_ROUTE socket and then prints out when RTM_NEWADDR, RTM_DELADDR and RTM_IFINFO are received.
What I notice is that when I use a single interface (wifi or ethernet cable) and disconnect the network adapter (disable wifi or unplug the cable) I get nothing at all. If I then reconnect (enable wifi or plug in the cable) I get RTM_NEWADDR but no RTM_IFINFO.
If I have both the wifi and the cable connected at the same time, both disconnecting and then reconnecting one of the interfaces (e.g. disable wifi then re-enable wifi) produces no events at all.
IPv6 seems to work. If I test IPv6 in the same manner, I get an RTM_NEWADDR on connection and RTM_DELADDR on disconnection (the address is the IPv6 link local address - my DHCP server does not serve up IPv6 addresses).
A couple of other side notes: If I try to do if_indextoname(), it doesn't always work. I need to insert a sleep to be able to consistently get the name back (I chose 500 milliseconds, I didn't spend any time trying other values to see if a lower value would work).
Also, if I call getifaddrs() in a loop (with a little sleeping between calls) after receiving the IPv6 RTM_NEWADDR event to try to find the missing IPv4 address, it can take a long time for it to show up in the returned data. I have seen it take up to 8 seconds on my system. Note that the IP address is up and usable long before this as a continuous ping to an external address readily confirmed.
I have tested this program on a MacBook Pro running 10.13, an iMac running 10.14 and a VM running 10.12 - all behave the same way.
So, my question is: is this a bug in the OS, or do I have a fundamental misunderstanding of how the PF_ROUTE socket is supposed to work?
Thanks,
Kevin
#include <SystemConfiguration/SystemConfiguration.h>
#include <net/route.h>
#include <errno.h>
struct cmn_msghdr
{
u_short msglen;
u_char version;
u_char type;
};
int main(int argc, const char * argv[])
{
char buf[1024];
size_t len;
int skt, family = AF_UNSPEC;
if ( argv[1] && argv[1][0] == '4' )
family = AF_INET;
else if ( argv[1] && argv[1][0] == '6' )
family = AF_INET6;
// Create a PF_ROUTE socket over which we will receive change messages
skt = socket( PF_ROUTE, SOCK_RAW, family );
if ( skt == -1 )
{
printf( "ERR: Failed to create PF_ROUTE socket. error %d\n", errno );
return -1;
}
printf( "Watching for %s address changes. Press Ctrl-C to exit\n",
family == AF_UNSPEC ? "IP" : ( family == AF_INET6 ? "IPv6" : "IPv4" ) );
// Loop forever waiting for messages
for (;;)
{
len = recv( skt, buf, sizeof(buf), 0 );
if ( len < 0 )
{
switch (errno)
{
case EINTR:
case EAGAIN:
printf( "ERR: EINTR or EAGAIN on PF_ROUTE socket\n" );
continue;
default:
printf( "ERR: Failed to receive on PF_ROUTE socket. error %d\n", errno );
continue;
}
}
if ( len < sizeof( cmn_msghdr ) )
{
printf( "ERR: Data received on PF_ROUTE socket too small: %ld bytes\n", len );
continue;
}
struct cmn_msghdr *hdr = (struct cmn_msghdr *)buf;
if ( hdr->version != RTM_VERSION )
{
printf( "ERR: RTM version %d is not supported\n", hdr->version );
continue;
}
switch( hdr->type )
{
case RTM_NEWADDR:
printf( "RTM_NEWADDR\n" );
break;
case RTM_DELADDR:
printf( "RTM_DELADDR\n" );
break;
case RTM_IFINFO:
printf( "RTM_IFINFO\n" );
break;
default:
// Don't care
continue;
}
}
return 0;
}

How to compare enum in arduino?

I am facing an issue with arduino, since I want to change the state of my device using an enum, but it doesn't seeem to work, my code looks like below. I am not entirely sure where it goes wrong, I think as well that the comparison between settingTo and toP2P could be wrong?
Thanks in advance!
String toP2P = "503250"
String toABP = "414250";
String settingTo = LoRa_Tx.dataRX.substring(indx);
if( settingTo == toP2P ) {
//switching to P2P
Serial.println("current mode 1 "+(String) LoRa_Tx.current_modeRxTx);
if(LoRa_Tx.current_modeRxTx != LoRa_Tx.LoRaMod){
LoRa_Tx.current_modeRxTx = LoRa_Tx.LoRaMod;
}
} else if(settingTo == toABP){
//switching to ABP
if(LoRa_Tx.current_modeRxTx != LoRa_Tx.LoRaWan){
LoRa_Tx.current_modeRxTx = LoRa_Tx.LoRaWan;}
}
}
My class has the enum defined as
typedef enum modeRxTx{LoRaMod, LoRaWan, Idle} ;
modeRxTx current_modeRxTx = Idle;
In general, you should avoid the String class, as it will eventually cause problems. However, given that the LoRa_Tx appears to have a String member, here is one way to watch for those two modes:
if ((indx > -1) && (LoRa_Tx.dataRx.length() >= indx+5)) {
const char *settingTo = &LoRa_Tx.dataRx.c_str()[ indx ];
if ( strncmp_P( settingTo, PSTR("503250"), 6 ) == 0 ) {
//switching to P2P
Serial.print( F("current mode 1 ") ); // <-- saves RAM!
Serial.println( LoRa_Tx.current_modeRxTx );
if(LoRa_Tx.current_modeRxTx != LoRa_Tx.LoRaMod) {
LoRa_Tx.current_modeRxTx = LoRa_Tx.LoRaMod;
}
} else if ( strncmp_P( settingTo, PSTR("414250"), 6 ) == 0 ) {
//switching to ABP
if(LoRa_Tx.current_modeRxTx != LoRa_Tx.LoRaWan) {
LoRa_Tx.current_modeRxTx = LoRa_Tx.LoRaWan;
}
}
}
Instead of creating a substring, it just makes a pointer to the actual characters of data_Rx. The c_str() function returns a pointer to the first character (zero-based index) or the String, and the [ indx ] is the first of the mode number characters. Finally, the & is a pointer to the first mode number character.
Next, it uses a standard library function, strncmp_P (documented here), to compare those mode number characters with the modes you are looking for, and it only compares up to 6 characters. You don't say if there's a delimiter after "503250", so I don't know if "50325076" is possible and should be rejected.
The strncmp_P expects to get a PROGMEM string as the second argument, not just a const char *, so that's what the PSTR macro does. This saves RAM because the PSTR will be stored and compared from FLASH memory (aka PROGMEM). The Serial.print statements should use the F() macro for the same reason.

Checking the environment variable without using GetLastError() == ERROR_ENVVAR_NOT_FOUND in VC++

Here is my code
#include<iostream>
#include<Windows.h>
using namespace std;
#define BUFSIZE 4602
#define VARNAME TEXT("zzz")
int main()
{
TCHAR chNewEnv[BUFSIZE];
GetEnvironmentVariable(VARNAME, chNewEnv, BUFSIZE);
if(GetLastError()==ERROR_ENVVAR_NOT_FOUND)
{
cout<<"Hello";
}
else
{
cout<<"Bye";
}
return 0;
}
I am checking whether zzz environment variable is available or not. I need to do the same without using if(GetLastError()==ERROR_ENVVAR_NOT_FOUND). Is there any way?
I am doing the same program using C# in which I don't use GetLastError(). I need to make these two programs similar that is why I am asking without the use of GetLastError(). Here is my C# snippet
string abc =Environment.GetEnvironmentVariable("zzz");
if (abc == null || abc.ToUpper() == "NULL" || abc.ToUpper() == "NUL")
You have to use GetLastError, as you may encounter cases where a variable is defined with no value.
Such cases can be emulated with:
SetEnvironmentVariable( "foo", "" );
When GetEnvironmentVariable return 0, it may be that the variable doesn't exist, or that is exists with an empty content. In the later case, GetLastError returns 0.
If you don't care (that is: a nonexistent variable or an empty one is the same for you), then don't bother with GetLastError.
Side note. You should always make a first call with a NULL nSize argument, as the documentation states:
If lpBuffer is not large enough to hold the data, the return value is
the buffer size, in characters, required to hold the string and its
terminating null character and the contents of lpBuffer are undefined.
And, I just tested (Windows 7), in such case GetLastError returns 0 (awkward, if you ask me)
Edit: On Windows 7, when I set an user variable to "null", the value retrieved by GetEnvironmentVariable (MBCS) is a 5 bytes string 'n', 'u', 'l', 'l', '\0'
Code (UNICODE build):
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <Windows.h>
#define BUFSIZE 4602
#define VARNAME L"zzz"
const wchar_t * MyGetEnv( const wchar_t * pszVarName, wchar_t * pszVarValue,
size_t cbValue ) {
DWORD dwCopied = GetEnvironmentVariable( pszVarName, pszVarValue, cbValue );
// The line bellow MAY BE COMMENTED OUT IF YOU REALLY
// DON'T LIKE GETLASTERROR, AS cbCopied WILL BE ZERO FOR
// NON-EXISTENT VARIABLE
if ( GetLastError() != NO_ERROR ) return NULL; // doesn't exist, or error
if ( dwCopied == 0 ) return NULL; // var is empty
return pszVarValue;
}
int main() {
wchar_t szVarValue[ BUFSIZE ];
const wchar_t * pszVarValue = MyGetEnv( VARNAME, szVarValue,
_countof( szVarValue ) );
if ( pszVarValue == NULL ) {
printf( "No variable or empty value\n" );
} else if ( ( _wcsicmp( pszVarValue, L"null" ) == 0 ) ||
( _wcsicmp( pszVarValue, L"nul" ) == 0 ) ) {
printf( "Special 'null' or 'nul' value\n" );
} else {
wprintf( L"Value is %s\n", szVarValue );
}
return 0;
}

Resources