expanding c code to be "more cross-platform"

L

lvirden

I'm responsible for supporting some C code which implements one time
passwords.

The author of the code is no longer available for help.

The code has been used for years on a sparc solaris platform.

However...

Now I need it to work on Linux Intel.

The behavior I am seeing is this.

I compiled this C code on Linux, using gcc, and it compiled without
complaint. I know that part of the code converts things into a hex
string from a long (and converts from the hex string into a long).

When I run a "build key" option of software using this code, it is
passed an IP address (for illustration, lets say"123.45.67.890").

A otp is generated.

Then, the command is issued with a flag that says "list the
information".
What it prints is
890.67.45.123

Now, what I am looking for are hints and tips on how to go about
generating things so that a command on either of the platforms will be
able to read and interpret correctly the information.

In other words, the data generated has to be readable across
platforms.

Are there some tutorials or guidelines for doing this sort of thing?
 
R

Roberto Waltman

lvirden said:
.... it is passed an IP address (for illustration, lets say"123.45.67.890").

890 is an interesting value for an octet...

In any case:

$ man htonl

BYTEORDER(3) Linux Programmer’s Manual BYTEORDER(3)

NAME
htonl, htons, ntohl, ntohs - convert values
between host and network byte order

SYNOPSIS
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
....

Roberto Waltman

[ Please reply to the group,
return address is invalid ]
 
L

lvirden

Thank you, Mr. Waltman, for taking the time to answer.

890 is an interesting value for an octet...

Sigh - sorry about that. Should have thought for more than a second
about the example...
In any case:

$ man htonl

BYTEORDER(3) Linux Programmer's Manual BYTEORDER(3)

NAME
htonl, htons, ntohl, ntohs - convert values
between host and network byte order

SYNOPSIS
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);


A couple of questions here.

1. So, I would take my long and "convert it" to network byte order,
write it out. Then I would use the network to host routine when
reading things back. Did I get that right?

2. I see 16 and 32 bit macros. What about 64 bit? The code I'm working
with has an #ifdef for 64 bit, so I'd want to deal with that as well...
 
B

Ben Bacarisse

lvirden said:
I'm responsible for supporting some C code which implements one time
passwords.

The code has been used for years on a sparc solaris platform.

However...

Now I need it to work on Linux Intel.

If at all possible, convert the code to portable C. You will then get
the same results on all machines. Where you can't, localise the
non-portable part into as few functions as possible and then re-write
these as required to get the desired behaviour.

It seems obvious, and I am sorry if I am telling you stuff you know,
but it is surprising how many people think that C is inherently
non-portable (especially if it is "doing stuff with bits").

If you can't post your code, can you say more about how the data is
manipulated? There is nothing in what you have said so far that
indicates any portability issues. With more information, we may be
able to suggest portable solutions, or at least point to the danger
areas.
 
R

Roberto Waltman

lvirden said:
A couple of questions here.

1. So, I would take my long and "convert it" to network byte order,
write it out. Then I would use the network to host routine when
reading things back. Did I get that right?

Not sure. Apparently you run into "endianness" problems, (see links
below.)
The memory ordering of bytes in larger data types changes from system
to system. Assuming you have a C compiler that has 8-bit chars and 32
bit integers, the value 0x01020304 could be stored in memory as:

(From lower byte address to higher byte address)
(a) 0x01, 0x02, 0x03, 0x04 ("Big Endian")
(b) 0x04, 0x03, 0x02, 0x01 ("Little Endian")
These are the two most common possibilities
(c) to (v) ... The other 22 combinations are possible.

In most cases you do not care what the byte ordering is.
Some exceptions are:
(a) Exchanging data with another computer which may use a different
ordering.
(b) Using a library that expects raw data using a different ordering
than the "natural" one for your machine.
(c) Programming tricks such as the following, that depend on a
particular layout/ordering:

union u {
char c;
int i;
};

union u tst;
tst.i = 0;
tst.c = 1;
printf("%d\n", tst.i); /* may or not print '1' */


In you original post you wrote:
------
When I run a "build key" option of software using this code, it is
passed an IP address (for illustration, lets say"123.45.67.890").
A otp is generated.
Then, the command is issued with a flag that says "list the
information". What it prints is 890.67.45.123
------
We can not help any further without seeing the source code and
understanding how that IP address is "passed". It may be used
somewhere assuming it is already in network order, because that
happens to be the native format for the machine in which the code was
originally developed.
In the general case, you should keep the data in its native format and
convert (forth and back) to whatever is appropriate at the last moment
before calling functions the require it.
The hton family of functions do that for the "network" ordering
expected by TCP/IP implementations. (That's short for "host to
network")
2. I see 16 and 32 bit macros. What about 64 bit? The code I'm working
with has an #ifdef for 64 bit, so I'd want to deal with that as well...

See XDR, RFC4506 - Search also for "marshaling", "serialization", etc.

Warning: As many regulars here will be happy to point out, while posts
dealing with general portability problems are on topic in comp.lang.c,
the moment you start dealing with particular implementations they
became taboo, and they may say nasty things to you.
If fixing your problem requires knowledge of the Solaris & Linux
platforms, (as opposed to generic C questions,) you may be better of
in a group dedicated to Solaris/Linux/networking.


Links:

http://en.wikipedia.org/wiki/Endianess
http://3bc.bertrand-blanc.com/endianness05.pdf
http://www.netrino.com/Publications/Glossary/Endianness.php
http://www.codeproject.com/cpp/endianness.asp
http://www.faqs.org/rfcs/rfc4506.html

Roberto Waltman

[ Please reply to the group,
return address is invalid ]
 
L

lvirden

If you can't post your code,

I don't think I had better - can't violate certain contractual
agreements, etc.
can you say more about how the data is
manipulated? There is nothing in what you have said so far that
indicates any portability issues.

Well, there's a whole library of code here. But what I see is a setup
command which is passed several arguments, one of which is an unsigned
long IP address.
The ip address is passed, along with a variety of other arguments,
into an encoding routine. In the encoding routine, the ip address is
passed like this:

otp_uln2hex(&otp->ip, OTP_IP_BYTES, ptr);
ptr += OTP_IP_LEN;
*ptr++ = ' ';
*ptr++ = '0' + otp->sequence / 10000;
*ptr++ = '0' + (otp->sequence / 1000) % 10;
*ptr++ = '0' + (otp->sequence / 100) % 10;
*ptr++ = '0' + (otp->sequence / 10) % 10;
*ptr++ = '0' + otp->sequence % 10;
*ptr++ = ' ';

Where ptr is a pointer into a line. when all the processing is done,
there's one ascii line which is output to a file, which then other
processes read.

Inside the uln2hex routine, the pointer to an unsigned long, along
with a count and an output area is taken, and while the count is
greater than 0, the routine loops around a switch statement. The cases
are the bytes remaining.
For each byte, a line like this is executed:

case 1:
*ptr++ = hex[(*in >> 4) & 0xf];
*ptr++ = hex[*in & 0xf];

where hex[] is an array of hex ascii characters.

Does that help?
 
L

lvirden

If fixing your problem requires knowledge of the Solaris & Linux
platforms, (as opposed to generic C questions,) you may be better of
in a group dedicated to Solaris/Linux/networking.

I certainly wouldn't want to cause any problems, having read this
newsgroup off and on for many, many years.

Is there a group/mailing list/web forum dedicated to solaris/linux
networking issues where problems such as mine is more appropriate?

As for showing code. I wish I could. However, non-disclosure contracts
really keep me from showing the code.
 
K

Keith Thompson

lvirden said:
Is there a group/mailing list/web forum dedicated to solaris/linux
networking issues where problems such as mine is more appropriate?

Probably comp.unix.programmer.
 
C

CBFalconer

lvirden said:
I'm responsible for supporting some C code which implements one time
passwords. The author of the code is no longer available for help.
The code has been used for years on a sparc solaris platform.

Now I need it to work on Linux Intel.

The behavior I am seeing is this.

I compiled this C code on Linux, using gcc, and it compiled without
complaint. I know that part of the code converts things into a hex
string from a long (and converts from the hex string into a long).

When I run a "build key" option of software using this code, it is
passed an IP address (for illustration, lets say "123.45.67.890").

A otp is generated.

Whatever that is. It has nothing to do with the problem.
Then, the command is issued with a flag that says "list the
information". What it prints is

890.67.45.123

Quite obviously you have run into something to do with endianism.
Somewhere you are treating the data as a sequence of bytes, rather
than as a long. In other words, invalid code, and it is biting. I
suspect you are also assuming 8 bit bytes, which may also bite in
the future.

You want to use an unsigned long to hold the value. Install things
with modulo 256 arithmetic and shifts, and extract things with
divisions by 256.
 
B

Ben Bacarisse

lvirden said:
If you can't post your code,

I don't think I had better - can't violate certain contractual
agreements, etc.
can you say more about how the data is
manipulated? There is nothing in what you have said so far that
indicates any portability issues.

Well, there's a whole library of code here. But what I see is a setup
command which is passed several arguments, one of which is an unsigned
long IP address.
The ip address is passed, along with a variety of other arguments,
into an encoding routine. In the encoding routine, the ip address is
passed like this:

otp_uln2hex(&otp->ip, OTP_IP_BYTES, ptr);
ptr += OTP_IP_LEN;
*ptr++ = ' ';
*ptr++ = '0' + otp->sequence / 10000;
*ptr++ = '0' + (otp->sequence / 1000) % 10;
*ptr++ = '0' + (otp->sequence / 100) % 10;
*ptr++ = '0' + (otp->sequence / 10) % 10;
*ptr++ = '0' + otp->sequence % 10;
*ptr++ = ' ';

Where ptr is a pointer into a line. when all the processing is done,
there's one ascii line which is output to a file, which then other
processes read.

Inside the uln2hex routine, the pointer to an unsigned long, along
with a count and an output area is taken, and while the count is
greater than 0, the routine loops around a switch statement. The cases
are the bytes remaining.
For each byte, a line like this is executed:

case 1:
*ptr++ = hex[(*in >> 4) & 0xf];
*ptr++ = hex[*in & 0xf];

The only truly suspicious thing here is treating an IP address as an
unsigned long. That is not necessarily wrong, but it might be. I'd
be tempted to do a quick test: if you enter 1.2.3.4 as the address (on
Solaris) do you get the same encoding as you get when you enter
4.3.2.1 on the Linux implementation?

If so, the byte order is the issue, and you need to look at the code
path between entering the address and it becoming unsigned long * in
otp_uln2hex.
 
L

lvirden

The only truly suspicious thing here is treating an IP address as an
unsigned long. That is not necessarily wrong, but it might be. I'd
be tempted to do a quick test: if you enter 1.2.3.4 as the address (on
Solaris) do you get the same encoding as you get when you enter
4.3.2.1 on the Linux implementation?

Okay, here's the weird thing. I put in 1.2.3.4 as an address to be
encoded, on Linux. I then, on Solaris, indicated that I wanted to
decode the value. It got the right information with no coding
problem. So it looks like my library is doing the right thing! Now I
have to back track to figure out where the information is being used -
because something definitely isn't using the encoded address
correctly.

Thanks for the ideas!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top