How does gcc/cygwin get the DNS server? - c

I have some code I'm writing under cygwin (using GCC) that successfully uses gethostbyname(); however when I try to use the resolver directly to retrieve the IP address of the DNS server it fails (all entries in nsaddr_list[] are null and nscount is -1).
If gethostbyname() is working, then obviously it is able to connect to the DNS server.
This code...
if (res_init() == -1) {
fprintf(stderr,"res_init() failed\n");
exit(1);
}
if (_res.nscount <= 0) {
fprintf(stderr,"nscount = %d\n",_res.nscount);
}
else {
for(i=0;i<_res.nscount;i++) {
fprintf(stderr, "dnssrvr: %d.%d.%d.%d\n",
(_res.nsaddr_list[i].sin_addr.s_addr & 0xff) >> 0,
(_res.nsaddr_list[i].sin_addr.s_addr & 0xff00) >> 8,
(_res.nsaddr_list[i].sin_addr.s_addr & 0xff0000) >> 16,
(_res.nsaddr_list[i].sin_addr.s_addr & 0xff000000) >> 24);
}
}
works on unix/linux, but returns nscount=-1 on cygwin.
Is there some trick to getting the DNS server when using cygwin/gcc?

res_init does not necessarily populate _res.nsaddr_list. Instead, on Windows it directs the resolver to use DnsQuery_A, unless you have the resolv.conf file, in which case DNS servers from that file are used.
See the source here, files minires.c and minires-os-if.c.
If you need to know your DNS servers, you probably have to use DnsQueryConfig or GetNetworkParams.
NB: _res is undocumented and should not be used.
UPDATE Apparently the "newer" (ca 2010 and later) versions of cygwin do populate _res.nsaddr_list, via a call to get_dns_info and then get_registry_dns. You may want to make sure that you have the newest cygwin, and if the problem persists, try to use a debug version and trace calls to the mentioned functions.
UPDATE 2 No, _res is not populated, my mistake.

As n.m. says, on Cygwin res_init() does not populate _res.nsaddr_list if it is using the Windows resolver. It uses the Windows resolver if either /etc/resolv.conf does not exist, or /etc/resolv.conf contains options osquery.
In my opinion this is a Cygwin bug - returning a negative nscount is bogus - but nonetheless we are stuck with working around it.
The solution is to call GetNetworkParams() just as Cygwin does itself - here's what I'm doing as a fallback:
#include <windows.h>
#include <iphlpapi.h>
#include <netinet/in.h>
#include <arpa/inet.h>
if (_res.nscount < 0)
{
ULONG buflen = 0;
FIXED_INFO *buf = NULL;
if (GetNetworkParams(NULL, &buflen) == ERROR_BUFFER_OVERFLOW)
buf = malloc(buflen);
if (buf && GetNetworkParams(buf, &buflen) == NO_ERROR)
{
_res.nscount = 1;
_res.nsaddr_list[0].sin_family = AF_INET;
_res.nsaddr_list[0].sin_addr.s_addr = inet_addr(buf->DnsServerList.IpAddress.String);
_res.nsaddr_list[0].sin_port = htons(53);
}
free(buf);
}
You need to link against -liphlpapi for the GetNetworkParams() function.
This only takes the first Windows DNS address, but if you want the rest of them you can follow the linked list that GetNetworkParams() returns. GetNetworkParams() only returns IPv4 addresses, I'm not sure what you're supposed to do if the machine has an IPv6 DNS server address configured.

Related

Debug Info: Run-Time Check Failure #2 - Stack around the variable 'IpNetRow2' was corrupted

I have been trying to use GetIpNetEntry2 and ResolveIpNetEntry2 functions to get MAC address from a IPv4 in Visual Studio 2017 (SDK 8.1) on Windows 10 (Enterprise 1803). I have been getting:
Run-Time Check Failure #2 - Stack around the variable 'IpNetRow2' was corrupted.
by the end of the following function:
static int GetMacAddress(SOCKADDR_IN ClientAddr)
{
MIB_IPNET_ROW2 IpNetRow2 = { 0 };
DWORD dwBestIfIndex = 0;
int nRet = -1;
// Retrieves the index of the interface that has the best route to the client IP address.
GetBestInterface(inet_addr(inet_ntoa(ClientAddr.sin_addr)), &dwBestIfIndex);
// Finds MAC address from the local computer
IpNetRow2.InterfaceIndex = dwBestIfIndex;
IpNetRow2.Address.si_family = AF_INET;
IpNetRow2.Address.Ipv4.sin_addr.s_addr = ClientAddr.sin_addr.S_un.S_addr;
nRet = GetIpNetEntry2(&IpNetRow2);
if (nRet != NO_ERROR)
{
// Could not find the MAC address in the cache, lets hit the wire.
nRet = ResolveIpNetEntry2(&IpNetRow2, NULL);
}
return nRet;
}
Returned results are valid with nRet = 0 and dwBestIfIndex = 22.
I have searched as much as I can for a description of how to diagnose the issue; however I have not found any solution that is relevant. The code is so simple and I have on idea on what part of the code would cause such issue.
Thank you all for your help. I really appreciate it. Eventually I figured it out. It is not a code issue but a configuration issue. The Struct Member Alignment (Visual Studio Project Property -> C/C++ -> Code Generation) was set to 1 byte which caused the issue. I changed it back to 'Default' then the exception is gone. Learned a lesson. Thank you.

SDLNet Networking Not Working

I am working on a game written in C using SDL. Given that it already uses SDL, SDL_image, and SDL_ttf, I decided to add SDL_mixer and SDL_net to my engine. Getting SDL_mixer set up and working was very easy, but I am having a lot of trouble with SDL_net.
To test I created a very simple application with the following rules:
Run without arguments act as a TCP server on port 9999
Run with an argument try to connect to the server at the given IP address on port 9999
Here are some of the key lines of the program (I'm not going to post my whole event-driven SDL engine because its too long):
char *host = NULL;
if (argc > 1) host = argv[1];
and...
IPaddress ip;
TCPsocket server = NULL;
TCPsocket conn = NULL;
if (host) { /* client mode */
if (SDLNet_ResolveHost(&ip,host,port) < 0)
return NULL; //this is actually inside an engine method
if (!(conn = SDLNet_TCP_Open(&ip)))
return NULL;
} else { /* server mode */
if (SDLNet_ResolveHost(&ip,NULL,port) < 0)
return NULL;
if (!(server = SDLNet_TCP_Open(&ip)))
return NULL;
}
and... inside the event loop
if (server) {
if (!conn)
conn = SDLNet_TCP_Accept(server);
}
if (conn) {
void *buf = malloc(size); //server, conn, size are actually members of a weird struct
while (SDLNet_TCP_Recv(conn,buf,size))
onReceive(buf); //my engine uses a callback system to handle things
free(buf);
}
The program seems to start up just fine. However for some reason when I run it in client mode trying to connect to my home computer (which I have on a different IP) from my laptop I find that the call to SDLNet_TCP_Open blocks the program for awhile (5-10 seconds) then returns NULL. Can anybody see what I did wrong? Should I post more of the code? Let me know.

SCTP multi-homing not working as expected

I'm facing a problem in implementing multi-homing in SCTP as the server side. The server has 2 IPs it is listening to.
I'm almost there, but there are 2 problems:
First IP returns the INIT-ACK with 2 different IPs inside the header as it should but the other IP return twice the same IP in the INIT-ACK header.
Seems like I’m not supporting 100 % in multi-homing, for example, if one of the links is down I don’t see a fail over.
So I don’t know if except the setsockopt with the option SCTP_SOCKOPT_BINDX_ADD I need anything else (maybe SCTP_PRIMARY_ADDR?) or what is wrong in my implementation.
Following is the code, I enter this code twice, first time I do bind and then save the socket and the first address, second time again bind (for the second IP) and then running setsockopt for both of the addresses and sockets.
bind(socket, &sock_addr.addr.sock_addr, sock_addr_len);
if(SHARED.num_used_entries_in_sockaddr_array == 0)
{
SHARED.saved_socket = socket;
SHARED.sockaddr_array[1] = sock_addr.addr.sock_addr;
}
else
{
SHARED.sockaddr_array[0] = sock_addr.addr.sock_addr;
}
if(SHARED.num_used_entries_in_sockaddr_array > 0)
{
sock_rc = setsockopt(SHARED.saved_socket,
IPPROTO_SCTP,
SCTP_SOCKOPT_BINDX_ADD,
(char*)SHARED.sockaddr_array,
sizeof(SCKOS_SOCK_ADDR));
sock_rc = setsockopt(socket,
IPPROTO_SCTP,
SCTP_SOCKOPT_BINDX_ADD,
(char*)SHARED.sockaddr_array,
sizeof(SCKOS_SOCK_ADDR));
}
SHARED.num_used_entries_in_sockaddr_array++;
Thanks!!!
SCTP_SOCKOPT_BINDX_ADD is ok for multi-homing. your codes have some unused lines.
if(SHARED.num_used_entries_in_sockaddr_array == 0)
{
bind(socket, &sock_addr.addr.sock_addr, sock_addr_len);
}
else
{
sock_rc = setsockopt(socket,
IPPROTO_SCTP,
SCTP_SOCKOPT_BINDX_ADD,
(char*)sock_addr.addr.sock_addr,
sizeof(SCKOS_SOCK_ADDR));
}enter code here
You can refer to linux sctp implemantation. Did you see heartbeat on all paths? Which box you test on?
thanks for the answer, in the end I used sctp_bindx which is much easier to implement

How do you check if a directory exists on Windows in C?

Question
In a Windows C application I want to validate a parameter passed into a function to ensure that the specified path exists.*
How do you check if a directory exists on Windows in C?
*I understand that you can get into race conditions where between the time you check for the existance and the time you use the path that it no longer exists, but I can deal with that.
Additional Background
Knowing explicitly that a directory does or does not exist can get tricky when permissions come into play. It's possible that in attempting to determine if the directory exists, the process doesn't have permissions to access the directory or a parent directory. This is OK for my needs. If the directory doesn't exist OR I can't access it, both are treated as an invalid path failure in my application, so I don't need to differentiate. (Virtual) bonus points if your solution provides for this distinction.
Any solution in the C language, C runtime library, or Win32 API is fine, but ideally I'd like to stick to libraries that are commonly loaded (e.g. kernel32, user32, etc.) and avoid solutions that involve loading non-standard libraries (like PathFileExists in Shlwapi.dll). Again, (Virtual) bonus points if your solution is cross-platform.
Related
How can we check if a file Exists or not using Win32 program?
Do something like this:
BOOL DirectoryExists(LPCTSTR szPath)
{
DWORD dwAttrib = GetFileAttributes(szPath);
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
The GetFileAttributes() method is included in Kernel32.dll.
Here's a totally platform-agnostic solution (using the standard C library)
Edit: For this to compile in Linux, replace <io.h> with <unistd.h> and _access with access. For a real platform agnostic solution, use the Boost FileSystem library.
#include <io.h> // For access().
#include <sys/types.h> // For stat().
#include <sys/stat.h> // For stat().
bool DirectoryExists( const char* absolutePath ){
if( _access( absolutePath, 0 ) == 0 ){
struct stat status;
stat( absolutePath, &status );
return (status.st_mode & S_IFDIR) != 0;
}
return false;
}
A Windows-specific implementation that supports both MBCS and UNICODE builds:
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <tchar.h>
BOOL directory_exists( LPCTSTR absolutePath )
{
if( _taccess_s( absolutePath, 0 ) == 0 )
{
struct _stat status;
_tstat( absolutePath, &status );
return (status.st_mode & S_IFDIR) != 0;
}
return FALSE;
}
If linking to the Shell Lightweight API (shlwapi.dll) is ok for you, you can use the PathIsDirectory function.
Another option is the shell function PathFileExists()
PathFileExists() documentation
This function "Determines whether a path to a file system object such as a file or directory is valid."
So, this question is full of edge cases. A real answer would be like this:
BOOL DirectoryExists(LPCTSTR szPath, BOOL *exists)
{
*exists = FALSE;
size_t szLen = _tcslen(szPath);
if (szLen > 0 && szPath[szLen - 1] == '\\') --szLen;
HANDLE heap = GetProcessHeap();
LPCTSTR szPath2 = HeapAlloc(heap, 0, (szlen + 3) * sizeof(TCHAR));
if (!szPath2) return FALSE;
CopyMemory(szPath2, szPath, szLen * sizeof(TCHAR));
szPath2[szLen] = '\\';
szPath2[szLen + 1] = '.';
szPath2[szLen + 2] = 0;
DWORD dwAttrib = GetFileAttributes(szPath2);
HeapFree(heap, 0, szPath2);
if (dwAttrib != INVALID_FILE_ATTRIBUTES) {
*exists = TRUE; /* no point checking FILE_ATTRIBUTE_DIRECTORY on "." */
return TRUE;
}
/*
* If we get anything other than ERROR_PATH_NOT_FOUND then something's wrong.
* Could be hardware IO, lack of permissions, a symbolic link pointing to somewhere
* you don't have access, etc.
*/
return GetLastError() != ERROR_PATH_NOT_FOUND;
}
The correct result of DirectoryExists is tri-state. There are three cases and you need to handle all of them. Either it exists, it doesn't exist, or you were unable to check. I've lost data in production because a SAN returned NO to the check function and then let me create something clobbering it on the very next line of code. Don't make the same mistake Microsoft did when designing the File.Exists() API in C#.
However I see a lot of checks that are supliferous. Don't write thinks like if (directory doesn't exist) CreateDirectory(); just write CreateDirectory() if (GetLastError() != 0 && GetLastError() != ERROR_ALREADY_EXISTS. Or the converse with RemoveDirectory(); just remove it and check for either no error or path not found.

How do I create a "netlink" between kernel and userspace?

I want to use netlink to communicate between an application and kernel space. My Linux kernel version is 2.6.28, and the following is my wrong code:
nf_sock=netlink_kernel_create(NL_PROTO,0,nl_user_skb,THIS_MODULE);
The abbreviated error message is:
error: too few arguments to function 'netlink_kernel_create'
In the file <linux/netlink.h>, the function netlink_kernel_create() is defined as
extern struct sock *netlink_kernel_create(struct net *net,int unit,unsigned int groups,void (*input)(struct sk_buff *skb),struct mutex *cb_mutex,struct module *module)
I don't understand what to use for the first argument, net. Can someone explain what I should use here?
A struct net contains information about the network namespace, a set of network resources available to processes. Note that there could be multiple network namespaces (i.e. multiple instances of the networking stack), but most drivers use the init_net namespace.
Your call should probably look something like the following
nf_sock = netlink_kernel_create(&init_net,
NETLINK_USERSOCK,
0,
nl_rcv_func,
NULL,
THIS_MODULE);
where nl_rcv_func is a function taking struct sk_buff *skb as the only argument and processes the received netlink message.
You seem to have been following a guide such as this one, which (being from 2005) might well have been outpaced by the development of the kernel. It seems the internal API to create a netlink from the kernel side has changed.
Either check the Documentation/ folder in your local kernel tree for some (hopefully fresher) documentation, or read the code itself. You could also trawl the Linux Kernel mailing list archives for any mention of the changes that seem to have happened.
Here is the actual implemntation as of 2.6.29, if you'd rather puzzle it out backwards (and haven't already checked this in your own tree, of course).
Yes, struct net is indeed for net namespace, but it is not proper to always use init_net, you should register your own pernet_operations, like this:
static struct pernet_operations fib_net_ops = {
.init = fib_net_init,
.exit = fib_net_exit,
};
static int __net_init fib_net_init(struct net *net)
{
int error;
#ifdef CONFIG_IP_ROUTE_CLASSID
net->ipv4.fib_num_tclassid_users = 0;
#endif
error = ip_fib_net_init(net);
if (error < 0)
goto out;
error = nl_fib_lookup_init(net);
if (error < 0)
goto out_nlfl;
error = fib_proc_init(net);
if (error < 0)
goto out_proc;
out:
return error;
out_proc:
nl_fib_lookup_exit(net);
out_nlfl:
ip_fib_net_exit(net);
goto out;
}
static int __net_init nl_fib_lookup_init(struct net *net)
{
struct sock *sk;
struct netlink_kernel_cfg cfg = {
.input = nl_fib_input,
};
sk = netlink_kernel_create(net, NETLINK_FIB_LOOKUP, &cfg);
if (sk == NULL)
return -EAFNOSUPPORT;
net->ipv4.fibnl = sk;
return 0;
}
and finally:
register_pernet_subsys(&fib_net_ops);
I would suggest ioctl for kernel/user communication. The ioctl interface is standard and the chance of been updated between kernels is small.

Resources