How does a Perl socket resolve hostnames under Linux? - linux

I have a (from what I can tell) perfectly working Linux setup (Ubuntu 8.04) where all tools (nslookup, curl, wget, firefox, etc) are able to resolve addresses. Yet, the following code fails:
$s = new IO::Socket::INET(
PeerAddr => 'stackoverflow.com',
PeerPort => 80,
Proto => 'tcp',
);
die "Error: $!\n" unless $s;
I verified the following things:
Perl is able to resolve addresses with gethostbyname (ie the code below works):
my $ret = gethostbyname('stackoverflow.com');
print inet_ntoa($ret);
The original source code works under Windows
This is how it supposed to work (ie. it should resolve hostnames), since LWP tries to use this behavior (in fact I stumbled uppon the problem by trying to debug why LWP wasn't working for me)
Running the script doesn't emit DNS requests (so it doesn't even try to resolve the name). Verified with Wireshark

From a quick look, the following code from IO::Socket::INET
sub _get_addr {
my($sock,$addr_str, $multi) = #_;
my #addr;
if ($multi && $addr_str !~ /^\d+(?:\.\d+){3}$/) {
(undef, undef, undef, undef, #addr) = gethostbyname($addr_str);
} else {
my $h = inet_aton($addr_str);
push(#addr, $h) if defined $h;
}
#addr;
}
suggests (if you look at the caller of this code) the work-around of adding MultiHomed => 1, to your code.
Without that work-around, the above code appears to try to call inet_aton("hostname.com") using the inet_aton() from Socket.pm. That works for me in both Win32 and Unix, so I guess that is where the breakage lies for you.
See Socket.xs for the source code of inet_aton:
void
inet_aton(host)
char * host
CODE:
{
struct in_addr ip_address;
struct hostent * phe;
if (phe = gethostbyname(host)) {
Copy( phe->h_addr, &ip_address, phe->h_length, char );
} else {
ip_address.s_addr = inet_addr(host);
}
ST(0) = sv_newmortal();
if(ip_address.s_addr != INADDR_NONE) {
sv_setpvn( ST(0), (char *)&ip_address, sizeof ip_address );
}
}
It appears that the Perl gethostbyname() works better than the C gethostbyname() for you.

Could you perhaps tells us exactly how your code fails? You've got error checking code in there but you haven't reported what the error is!
I've just tried the original code (with the addition of the "use IO::Socket::INET" on my Mac OS X machine and it works fine.
I suspect that the Multihomed option is an unnecessary hack and some other issue is the root cause of your problem.

Make sure that you have the statement
use IO::Socket::INET;
At the beginning of your source code. If you leave this out, you are probably getting the error message:
Can't locate object method "new" via
package "IO::Socket::INET"
Beyond that you might verify that DNS is working using Net::DNS::Resoler, see more information here.
use Net::DNS;
my $res = Net::DNS::Resolver->new;
# Perform a lookup, using the searchlist if appropriate.
my $answer = $res->search('example.com');

Related

gethostbyname_r() calls for non-resolvable hosts always returning with h_errnop set to TRY_AGAIN (on Ubuntu 19.04 and 20.04)

while(do_again) {
rc = gethostbyname_r(host, &hbuf, tmp, TMPLEN, &hp, &my_h_errno);
if (rc != 0) {
if(my_h_errno == TRY_AGAIN) {
printf("HOST NOT FOUND: got a TRY_AGAIN response from gethostbyname_r()\n");
continue;
} else if (my_h_errno == HOST_NOT_FOUND) {
printf("HOST NOT FOUND: Got an authoritative answer\n");
exit(0);
} else {
printf("Other errors..\n");
exit(0);
}
}
do_again = false;
}
Above code always returning with my_h_errno set to TRY_AGAIN for non-resolvable hostnames on Ubuntu 19.04 and 20.04. On previous versions of the OS or on other Linux flavors, it usually returns a more authoritative version, HOST_NOT_FOUND.
How does the call(gethostbyname_r) exactly work? And are there any changes on the dns or lookup mechanism on the latest versions of Ubuntu which is causing such behavior?
[Update]
Installing libnss-myhostname which kind of acts as a fallback mechanism for hostname resolving, fixed the issue.
Debugging on the glibc code got me to the point where there was a call to the above library functions, if exsits, where the h_errno was set to HOST_NOT_FOUND for non-resolvable hosts. That was initially being set to TRY_AGAIN in the glibc's gethostbyname_r functionality for such cases.
From the manual
The gethostbyname*(), gethostbyaddr*(), herror(), and hstrerror()
functions are obsolete. Applications should use getaddrinfo(3),
getnameinfo(3), and gai_strerror(3) instead.

Cleanest way to fix Windows 'filename too long' CI tests failure in this code?

I'm trying to solve this Windows filename issue
Basically our CI job fails, with the 'filename too long' error for Windows.
warning: Could not stat path 'node_modules/data-validator/tests/data/data-examples/ds000247/sub-emptyroom/ses-18910512/meg/sub-emptyroom_ses-18910512_task-noise_run-01_meg.ds/sub-emptyroom_ses-18910512_task-noise_run-01_meg.acq': Filename too long
I've read the docs for Node's path module, which seems like a possible solution. I also read about a Windows prefix (\\?\) to bypass the MAX_PATH...but have no idea how to implement these in a clean way.
This part of the codebase with the tests that are failing. The hardcoded path (testDatasetPath) is likely part of the problem.
function getDirectories(srcpath) {
return fs.readdirSync(srcpath).filter(function(file) {
return (
file !== '.git' && fs.statSync(path.join(srcpath, file)).isDirectory()
)
})
}
var missing_session_files = //array of strings here
const dataDirectory = 'data-validator/tests/data/'
function createDatasetFileList(path) {
const testDatasetPath = `${dataDirectory}${path}`
if (!isNode) {
return createFileList(testDatasetPath)
} else {
return testDatasetPath
}
}
createFileList function
function createFileList(dir) {
const str = dir.substr(dir.lastIndexOf('/') + 1) + '$'
const rootpath = dir.replace(new RegExp(str), '')
const paths = getFilepaths(dir, [], rootpath)
return paths.map(path => {
return createFile(path, path.replace(rootpath, ''))
})
}
tl;dr A GitLab CI Job fails on Windows because the node module filenames become too long. How can I make this nodejs code OS agnostic?
This is a known error in the Windows Environment, however, there is a fix..
If you're using an NTFS based filesystem, you should be able to enable long paths in
Local Computer Policy > Computer Configuration > Administrative Templates > System > Filesystem > NTFS
This is also specified in the document you just linked, and theoretically, should work. However, the path shouldn't really be longer than 32 bits,
This will come with some performance hits, but should get the job done.
Also, you should preferably switch to a better package or search for alternatives. If this is your own package, maybe restructure it?
Finally, if none of these work, move your project folder directly to your data drive, (C:\) this will cut down on the other nested and parent folders.
This is inherently bad, and you may run into issues during deployment, if you choose to do it.

Expanding / Resolving ~ in node.js

I am new to nodejs. Can node resolve ~ (unix home directory) example ~foo, ~bar
to /home/foo, /home/bar
> path.normalize('~mvaidya')
'~mvaidya'
> path.resolve('~mvaidya')
'/home/mvaidya/~mvaidya'
>
This response is wrong; I am hoping that ~mvaidya must resolve to /home/mvaidya
As QZ Support noted, you can use process.env.HOME on OSX/Linux. Here's a simple function with no dependencies.
const path = require('path');
function resolveHome(filepath) {
if (filepath[0] === '~') {
return path.join(process.env.HOME, filepath.slice(1));
}
return filepath;
}
The reason this is not in Node is because ~ expansion is a bash (or shell) specific thing. It is unclear how to escape it properly. See this comment for details.
There are various libraries offering this, most just a few lines of code...
https://npm.im/untildify ; doesn't do much more than os.homedir(), see index.js#L10
https://npm.im/expand-tilde ; basically uses os-homedir to achieve the same, see index.js#L12
https://npm.im/tilde-expansion ; this uses etc-passwd so doesn't seem very cross platform, see index.js#L21
So you probably want to do this yourself.
This NodeJS library supports this feature via an async callback. It uses the etc-passswd lib to perform the expansion so is probably not portable to Windows or other non Unix/Linux platforms.
https://www.npmjs.org/package/tilde-expansion
https://github.com/bahamas10/node-tilde-expansion
If you only want to expand the home page for the current user then this lighter weight API may be all you need. It's also synchronous so simpler to use and works on most platforms.
https://www.npmjs.org/package/expand-home-dir
Examples:
expandHomeDir = require('expand-home-dir')
expandHomeDir('~')
// => /Users/azer
expandHomeDir('~/foo/bar/qux.corge')
// => /Users/azer/foo/bar/qux.corge
Another related lib is home-dir that returns a user's home directory on any platform:
https://www.npmjs.org/package/home-dir
An example:
const os = require("os");
"~/Dropbox/sample/music".replace("~", os.homedir)
This is a combination of some of the previous answers with a little more safety added in.
/**
* Resolves paths that start with a tilde to the user's home directory.
*
* #param {string} filePath '~/GitHub/Repo/file.png'
* #return {string} '/home/bob/GitHub/Repo/file.png'
*/
function resolveTilde (filePath) {
const os = require('os');
if (!filePath || typeof(filePath) !== 'string') {
return '';
}
// '~/folder/path' or '~' not '~alias/folder/path'
if (filePath.startsWith('~/') || filePath === '~') {
return filePath.replace('~', os.homedir());
}
return filePath;
}
Uses more modern os.homedir() instead of process.env.HOME.
Uses a simple helper function that can be called from anywhere.
Has basic type checking. You may want to default to returning os.homedir() if a non-string is passed in instead of returning empty string.
Verifies that path starts with ~/ or is just ~, to not replace other aliases like ~stuff/.
Uses a simple "replace first instance" approach, instead of less intuitive .slice(1).
I just needed it today and the only less-evasive command was the one from the os.
$ node
> os.homedir()
'/Users/mdesales'
I'm not sure if your syntax is correct since ~ is already a result for the home dir of the current user
Today I used https://github.com/sindresorhus/untildify
I run on OSX, worked well.

AAssetManager_openDir crashing app on start

I'm trying to iterate through all the files contained in the assets folder. To get the working directory I use: AAssetManager_openDir. However simply having this in my code causes a crash on startup - android_main doens't even get a look in. Has anybody had similar problems and/or know how to resolve this?
const char* filename = (const char*)NULL;
const char* dirName = "";
AAssetDir* dir = AAssetManager_openDir(assetManager, dirName);
while((filename = AAssetDir_getNextFileName(dir)) != NULL)
{
//action per file
}
AAssetDir_close(dir);
Well I didn't have any luck getting it to work so I tried a different approach.
I compiled a static library of Minizip and, combined with Zlib, opened the APK file (the path of which found via JNIEnv) and found the filenames nestled within, skipping over entries not contained in the assets folder.
Roundabout way to do it but since AAssetManager_openDir isn't working this seemed like the only option.
Still it would be nice if somebody does find the "correct" solution.
const char* dirName = "";
Is probably what is causing the crash.
Instead try:
while(1)
{
const char* filename = AAssetDir_getNextFileName(dir);
if(!filename)
break;
}

unable to open sqlite3 database in QT

I am in the beginning phase of both sqlite, as well as QT.
There are various examples under QtSDK IDE for sqlite database. This is an excerpt from one of the examples :
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(":memory:");
if (!db.open()) {
QMessageBox::critical(0, qApp->tr("Cannot open database"),
qApp->tr("Unable to establish a database connection.\n"
"This example needs SQLite support. Please read "
"the Qt SQL driver documentation for information how "
"to build it.\n\n"
"Click Cancel to exit."), QMessageBox::Cancel);
return false;
}
This works just fine. but if I try to replace ":memory:" with an actual sqlite3 database file,...
void MainWindow::on_pushButton_5_clicked()
{
QSqlQuery query;
accounts_db = new QSqlDatabase();
*accounts_db = QSqlDatabase::addDatabase("QSQLITE"); perror("");
accounts_db->setDatabaseName("/home/aditya/test.db.sqlite");
QSqlError *a = new QSqlError();
*a = accounts_db->lastError();
perror(a->text().toLatin1());
if (!accounts_db->open()) {
perror("database open error :");
QMessageBox::critical(0,qApp->tr("db.open\n"),a->text(),QMessageBox::Cancel);
goto end; // quit if not successful
}
if ( !accounts_db->isOpen() ) {
perror("database is not open");
}
query.exec("select accno,branchcode,fname,lname,curbalance,accdate from accounts");
while(query.next()) {
QString str = query.value(0).toString();
std::cerr << qPrintable(str) << std::endl;
}
end:
;
}
...then I am not so lucky. Not sure exactly how lastError() works, but tried it anyway. I am getting these errors...
No such file or directory
: Invalid argument
QSqlQuery::exec: database not open
I have tried changing the permissions n'all on the concerned files and folders as suggested on different forums, but to no results. Just a note, I am working on Ubuntu Linux (...if that matters), I have also tested this test.db database file with the sqlite3 command line program, that works flawless.
Any guidance is appreciated...thanks.
EDIT:
Very sorry that I completely forgot to explain what the error is... :|
Created a brand new database, and it works !!!
not clear why it was not working before though... I definitely need to study more.
Anyway, thanks a lot ppl :) .

Resources