Modifying extension list in X509 certificate using OpenSSL in C - c

I am tring to insert signed sct into a precertificate that has a poison extension.
So I first remove the poison extension, then add the SCT.
This is what I've done:
int main(int argc, char **argv) {
size_t lenCert = 0, lenCert2 = 0;
char *filePEM = "testpem/precert3.cert.pem";
char *strCertPem = loadFileContent(filePEM, &lenCert);
const X509 *cert = parse_certificate(strCertPem);
X509 *certRef = X509_dup(cert);
if(!cert || cert==NULL){
printf("Failed parsing\n");
return -1;
}
int len_init = -1;
unsigned char *buf_init = NULL;
len_init = i2d_X509(certRef, &buf_init);
if(len_init < 0){
printf("INIT: failed conversion to DER\n");
return -1;
} else {
printf("INIT: Successful conversion to DER[%d]\n", len_init);
}
printf("size certificate: %ld\n", lenCert);
X509_EXTENSION *tmpext;
const STACK_OF(X509_EXTENSION) *allExt = X509_get0_extensions(cert);
const STACK_OF(X509_EXTENSION) *allExt2 = X509_get0_extensions(certRef);
int my_idx = X509v3_get_ext_by_NID(allExt, NID_ct_precert_poison, -1);
int idx = my_idx;
int cc = X509_get_ext_count(cert);
printf("Extension count in cert BEFORE = %d\n", cc);
printf((allExt==NULL) ? "Extensions extraction FAILED\n" : "Extensions extraction SUCCESS\n");
int counter = X509v3_get_ext_count(allExt);
printf("Extension[%d] count BEFORE = %d\n", idx, counter);
do {
tmpext = X509v3_get_ext(allExt, idx);
X509v3_delete_ext(allExt, idx);
X509_EXTENSION_free(tmpext);
idx = X509v3_get_ext_by_NID(allExt, NID_ct_precert_poison, -1);
printf("pass\n");
} while (idx != -1);
counter = X509v3_get_ext_count(allExt);
printf("Extension count AFTER = %d\n", counter);
if(X509_cmp( cert, certRef)){
printf("Certificate modified\n\n");
} else {
printf("FAILED!!! \n");
}
cc = X509_get_ext_count(cert);
printf("Extension count in cert AFTER = %d\n", cc);
int len_inter;
unsigned char *buf_inter = NULL;
len_inter = i2d_X509(cert, &buf_inter);
if(len_inter < 0){
printf("INTERMEDIATE: failed conversion to DER\n");
return -1;
} else {
printf("INTERMEDIATE: Successful conversion to DER[%d]\n", len_inter);
}
unsigned char *dersct;
size_t lenSCTList = 0;
char *b64SCTList = "BIF6AHgAdgCwzIPlpfl9a698CcwoSQSHKsfoixMsY1C3xv0m4WxsdwAAAWZ7z/DQAAAEAwBHMEUCIQDKJPPQhWqje1rQq+T06x0iNlLT7rX71k23VPZkhm/QCwIgfhwNK7izeq0fHAlu7HuYRjmvym51RRdlNWhd50LQdu4=";
int b64Res = Base64Decode(b64SCTList, &dersct, &lenSCTList); //Decodes a base64 encoded string
printf("size final SCT List: %ld\n", lenSCTList);
STACK_OF(SCT) * scts = d2i_SCT_LIST(NULL, (const unsigned char **) &dersct, lenSCTList);
if(scts==NULL){
printf("Could not convert SCT List!");
return -1;
}
printf("SCT List converted !\n");
ASN1_OCTET_STRING *val = ASN1_OCTET_STRING_new();
ASN1_OCTET_STRING_set(val, dersct, (int)lenSCTList);
X509_EXTENSION* extSCT = X509_EXTENSION_create_by_NID(NULL, NID_ct_precert_scts, 0, val);
if(extSCT){
printf("created extension\n");
} else {
printf("Failed to create extension\n");
return -1;
}
if( X509_add_ext(cert, extSCT, -1)) {
printf("Extension added\n");
// X509_EXTENSION_free(extSCT);
} else {
printf("failed to add extension\n");
return -1;
}
int len_final;
unsigned char *buf_final = NULL;
len_final = i2d_X509(cert, &buf_final);
if(len_final < 0){
printf("FINAL: failed conversion to DER\n");
return -1;
} else {
printf("FINAL: Successful conversion to DER[%d]\n", len_final);
}
BIO *Cout = BIO_new(BIO_s_mem());
PEM_write_bio_X509(Cout, cert);
char* data;
const long len = BIO_get_mem_data(Cout, &data);
cc = X509_get_ext_count(cert);
printf("Extension count in cert AFTER = %d\n", cc);
printf("\ndata[%ld]: \n%s\n\n", len, data);
BIO_free_all(Cout);
int my_idx2 = X509_get_ext_by_NID(cert, NID_ct_precert_poison, -1);
X509_EXTENSION* extPoison2 = X509_get_ext(cert, my_idx2);
if(!extPoison2){
printf("failed last extension[%d] extract \n ", my_idx2);
return -1;
} else {
printf("Succeeded last extension extract[%d]\n ", my_idx2);
}
return 0;
}
This code seems to work, all step are fine, problem is, the final certificate displayed, when I save it to a file and run the command:
openssl x509 -in cert.pem -noout -text
it is the same as the original precertificate, it contains the poison extension and no SCT.
Even comparing both files, they are identical.
Where did I go wrong?

Your main problem is that for X509 X509_CRL X509_REQ when created by parsing input i.e. not built up from scratch OpenSSL saves the tbs encoding and reuses it on output (and digesting and comparison, which is why your X509_cmp failed) even if you have changed some of the fields that go in that encoding, unless you sign the change(s) which you must to make the resulting object valid anyway. In short, you need to call X509_sign() or the extended form X509_sign_ctx() after making your changes.
After fixing that you have another problem: it does remove the poison ext and add an SCT ext -- which contains garbage, because your unnecessary call to d2i_SCT_LIST has changed the pointer you use. Removing that, plus your other unneeded cruft, produces the following code that works (with a precert and key of my own) to produce a correct-looking cert, though of course the SCTs you provided aren't valid for it:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/asn1.h>
#include <openssl/ct.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
int main(int argc, char **argv) {
// size_t lenCert = 0, lenCert2 = 0;
// char *filePEM = "testpem/precert3.cert.pem";
// replace mystery routines by one PEM:
FILE *infile = fopen(argv[1],"r"); if(!infile) exit(1);
X509 *cert = PEM_read_X509 (infile, NULL, NULL, NULL);
fclose(infile);
X509 *certRef = X509_dup(cert);
if(!cert || cert==NULL){ // redundant, and too late
printf("Failed parsing\n");
return -1;
}
#if 0 // useless
unsigned char *buf_init = NULL;
int len_init = i2d_X509(certRef, &buf_init);
#endif
//--printf("size certificate: %ld\n", lenCert);
//--X509_EXTENSION *tmpext;
const STACK_OF(X509_EXTENSION) *allExt = X509_get0_extensions(cert);
const STACK_OF(X509_EXTENSION) *allExt2 = X509_get0_extensions(certRef);
int my_idx = X509v3_get_ext_by_NID(allExt, NID_ct_precert_poison, -1);
int idx = my_idx;
#if 0
int cc = X509_get_ext_count(cert);
printf("Extension count in cert BEFORE = %d\n", cc);
//--printf((allExt==NULL) ? "Extensions extraction FAILED\n" : "Extensions extraction SUCCESS\n");
#endif
#if 0 // useless
int counter = X509v3_get_ext_count(allExt);
printf("Extension[%d] count BEFORE = %d\n", idx, counter);
#endif
#if 0
do {
X509_EXTENSION * tmpext = X509v3_get_ext(allExt, idx);
#endif
X509v3_delete_ext(allExt, idx);
#if 0
X509_EXTENSION_free(tmpext);
idx = X509v3_get_ext_by_NID(allExt, NID_ct_precert_poison, -1);
printf("pass\n");
} while (idx != -1);
printf("Extension count AFTER = %d\n", X509v3_get_ext_count(allExt));
#endif
#if 0
if(X509_cmp( cert, certRef)){
printf("Certificate modified\n\n");
} else {
printf("CENSORED \n");
}
printf("Extension count in cert AFTER = %d\n", X509_get_ext_count(cert));
#endif
#if 0 // useless
unsigned char *buf_inter = NULL;
int len_inter = i2d_X509(cert, &buf_inter);
#endif
unsigned char *dersct;
size_t lenSCTList = 0;
char *b64SCTList = "BIF6AHgAdgCwzIPlpfl9a698CcwoSQSHKsfoixMsY1C3xv0m4WxsdwAAAWZ7z/DQAAAEAwBHMEUCIQDKJPPQhWqje1rQq+T06x0iNlLT7rX71k23VPZkhm/QCwIgfhwNK7izeq0fHAlu7HuYRjmvym51RRdlNWhd50LQdu4=";
// replace mystery routine
dersct = malloc(strlen(b64SCTList)); // more than needed but convenient
lenSCTList = EVP_DecodeBlock(dersct, (unsigned char*)b64SCTList, strlen(b64SCTList));
printf("size final SCT List: %ld\n", lenSCTList);
#if 0 // useless and harmful
STACK_OF(SCT) * scts = d2i_SCT_LIST(NULL, (const unsigned char **) &dersct, lenSCTList);
if(scts==NULL){
printf("Could not convert SCT List!");
return -1;
}
printf("SCT List converted !\n");
#endif
ASN1_OCTET_STRING *val = ASN1_OCTET_STRING_new();
ASN1_OCTET_STRING_set(val, dersct, (int)lenSCTList);
free(dersct); // added
X509_EXTENSION* extSCT = X509_EXTENSION_create_by_NID(NULL, NID_ct_precert_scts, 0, val);
#if 0
if(extSCT){
printf("created extension\n");
} else {
printf("Failed to create extension\n");
return -1;
}
#endif
if( X509_add_ext(cert, extSCT, -1)) {
printf("Extension added\n");
// X509_EXTENSION_free(extSCT);
} else {
printf("failed to add extension\n");
return -1;
}
#if 0 // useless
unsigned char *buf_final = NULL;
int len_final = i2d_X509(cert, &buf_final);
#endif
// added
FILE * keyfile = fopen(argv[2],"r"); if(!keyfile) exit(2);
EVP_PKEY * signkey = PEM_read_PrivateKey (keyfile, NULL, NULL, NULL);
fclose(keyfile);
if( X509_sign(cert,signkey,EVP_sha256())<=0 ) exit(9);
BIO *Cout = BIO_new(BIO_s_mem());
PEM_write_bio_X509(Cout, cert);
char* data;
const long len = BIO_get_mem_data(Cout, &data);
printf("Extension count in cert AFTER = %d\n", X509_get_ext_count(cert));
printf("\ndata[%ld]: \n%s\n\n", len, data);
// added
FILE *outfile = fopen(argv[3],"w"); if(!outfile) exit(3);
fwrite(data,1,len,outfile); fclose(outfile);
BIO_free_all(Cout);
#if 0 // useless
int my_idx2 = X509_get_ext_by_NID(cert, NID_ct_precert_poison, -1);
X509_EXTENSION* extPoison2 = X509_get_ext(cert, my_idx2);
if(!extPoison2){
printf("failed last extension[%d] extract \n ", my_idx2);
return -1;
} else {
printf("Succeeded last extension extract[%d]\n ", my_idx2);
}
#endif
return 0;
}
However, modifying a value returned by get0 -- and 'discarding' the const on it -- is not good style, and might fail in some future implementation. It would be safer and also simpler to use X509_get_ext_by_NID and X509_delete_ext directly on cert.

Related

How to convert 24bit bitmap buffer to base64?

char * lpbitmap = NULL;
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 24;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
hDIB = GlobalAlloc(GHND, dwBmpSize);
lpbitmap = (char*)GlobalLock(hDIB);
I am using the windows GDI header file to create a bitmap, the goal is to convert the lpbitmap buffer to a base64 string so that it can be represented as such data:image/bmp;base64,Qk06
as far as I understand the bitmap is 24bit where every 3 chars represent RGB values example
How to convert each RGB value into a base64 value
Update
I changed the bitcount to 24
Below is my conversion of LihO's C++ answer to C: Base64 decode snippet in C++
As I mentioned in the comments, I thought it would be simple. But,there wasn't a simple cookbook to give to someone not familiar with C++. So, to do the conversion properly, I did it myself.
And, there were a few issues:
The answer did a lot of unnecessary buffer copying.
Using std::string and std::vector, although pretty fast, are somewhat slower than using raw byte buffers.
Using base64_chars.find is O(n) but if an inverse table is created/used, it can be O(1).
The answer did not support line breaks (e.g. \n) as most base64 programs do.
Anyway, below are the source files I came up with. They can be compiled with:
cc -I. -o b64test b64test.c base64.c dbgcom.c
Note that the mmap mode (i.e. -m) is untested.
FILE: base64.h
// base64.h -- base64 definitions
#ifndef _base64_h_
#define _base64_h_
#include <dbgcom.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
typedef struct {
u32 buf_opt; // buffer options
const char *buf_tag; // buffer name
byte *buf_base; // buffer pointer
size_t buf_len; // buffer length
int buf_fdmap; // mmap file descriptor
} b64buf_t;
#define B64BUFDEF(_sym) \
b64buf_t _sym = { .buf_tag = #_sym }
typedef struct {
//u32 ctl_opt; // options
const char *ctl_tag; // control name
b64buf_t ctl_inb; // input buffer
b64buf_t ctl_out; // output buffer
int ctl_wrapnow; // last output char was newline
int ctl_wraplen; // output wrap length
int ctl_wrapmax; // output wrap max
int ctl_fdchk; // file cross check
} b64ctl_t;
#define B64CTLDEF(_sym) \
b64ctl_t _sym = { .ctl_tag = #_sym }
#define B64MMAP (1u << 0) // 1=use mmap on output
#define B64TRIM (1u << 1) // 1=trim output file
// b64encode -- encode raw data to base64
// RETURNS actual output length
size_t
b64encode(b64ctl_t *ctl);
// b64decode -- decode base64 into raw binary
// RETURNS: actual output length
size_t
b64decode(b64ctl_t *ctl);
// b64bufdup -- safe buffer duplicate
void
b64bufdup(b64buf_t *dst,const b64buf_t *src);
// b64bufput -- output buffer to file
void
b64bufput(const b64buf_t *bufp,const char *ofile);
// b64bufget -- input file to buffer
void
b64bufget(b64buf_t *bufp,const char *ifile);
// b64setup -- passive setup
void
b64setup(b64ctl_t *ctl,u32 opt,const char *tag);
// b64init_encode -- initialize for encode
// RETURNS: 0=okay, -1=error
int
b64init_encode(b64ctl_t *ctl,const b64buf_t *inb,int wrapmax);
// b64init_decode -- initialize for decode
// RETURNS: 0=okay, -1=error
int
b64init_decode(b64ctl_t *ctl,b64buf_t *inb);
// b64destroy -- destroy/deinit output buffer
void
b64destroy(b64ctl_t *ctl);
// b64bufrls -- destroy/deinit output buffer
void
b64bufrls(b64buf_t *buf,const char *who);
#endif
FILE: base64.c
// base64.c -- base 64 encoder/decoder
#include <base64.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/mman.h>
static const char *base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
#ifndef B64DECODE_FAST
#define B64DECODE_FAST 1
#endif
#if B64DECODE_FAST
static byte decode_fast[256];
#endif
#define FMT "%2.2X/%c"
#define PRT(_c) _c,PRTABLE(_c)
#define PUT4(_idx,_val) \
arr4[_idx] = _val
#define PUTE(_idx,_val) \
arr4[_idx] = base64_chars[_val]
#define OUTLEN(_buf,_ptr) \
(_ptr - (byte *) (_buf)->buf_base)
// encode_xcheck -- cross-check encoding
#if B64ENCODE_XCHECK
#define encode_xcheck(_ctl,_arr4,_a4siz,_arr3,_a3siz,_pad) \
_encode_xcheck(_ctl,_arr4,_a4siz,_arr3,_a3siz,_pad)
static void
_encode_xcheck(b64ctl_t *ctl,
const byte *arr4,int a4siz,
const byte *arr3,int a3siz,int pad)
{
int xlen;
int oidx;
byte buf[a4siz];
dbgprt(ZPXHOWCHR,"encode_xcheck: ENTER a4siz=%d a3siz=%d pad=%d\n",
a4siz,a3siz,pad);
xlen = read(ctl->ctl_fdchk,buf,a4siz);
if (xlen != a4siz)
sysfault("encode_xcheck: read error -- a4siz=%d xlen=%d\n",
a4siz,xlen);
// check for error/mismatch
int errany = 0;
for (oidx = 0; oidx < a4siz; ++oidx) {
errany = (arr4[oidx] != buf[oidx]);
if (errany)
break;
}
do {
// only print if verbose mode or we have an error
if (! XVERBOSE) {
if (! errany)
break;
}
int totsiz = a4siz;
if (a3siz > totsiz)
totsiz = a3siz;
for (oidx = 0; oidx < totsiz; ++oidx) {
byte bp = 0;
byte a4 = 0;
byte a3 = 0;
size_t o4off = 0;
size_t o3off = 0;
if (oidx < a4siz) {
bp = buf[oidx];
a4 = arr4[oidx];
o4off = OUTLEN(&ctl->ctl_out,arr4);
}
if (oidx < a3siz) {
a3 = arr3[oidx];
o3off = OUTLEN(&ctl->ctl_inb,arr3);
}
int errcur = (arr4[oidx] != buf[oidx]);
printf("encode_xcheck: arr=%8.8zX/",o4off);
if (pad)
printf("censored");
else
printf("%8.8zX",o3off);
printf(" oidx=%d exp=" FMT " act=" FMT " arr3=" FMT,
oidx,PRT(bp),PRT(a4),PRT(a3));
if (errany)
printf(" ANY");
if (errcur)
printf("/CUR");
printf("\n");
}
if (errany)
sysfault("encode_xcheck: fault\n");
} while (0);
dbgprt(ZPXHOWCHR,"encode_xcheck: EXIT\n");
}
#else
#define encode_xcheck(_ctl,_arr4,_a4siz,_arr3,_a3siz,_pad) \
do { } while (0)
#endif
// encode_wrap -- perform encoding and inject newline
static inline_nodebug byte *
encode_wrap(b64ctl_t *ctl,byte *arr4)
{
dbgprt(ZPXHOWCHR,"encode_wrap: ENTER ctl_wraplen=%d ctl_wrapmax=%d\n",
ctl->ctl_wraplen,ctl->ctl_wrapmax);
do {
// bug out if wrap disabled
if (ctl->ctl_wrapmax <= 0)
break;
// wait for wrap
ctl->ctl_wraplen += 4;
ctl->ctl_wrapnow = 0;
if (ctl->ctl_wraplen < ctl->ctl_wrapmax)
break;
ctl->ctl_wraplen = 0;
ctl->ctl_wrapnow = 1;
PUT4(0,'\n');
encode_xcheck(ctl,arr4,1,NULL,0,-1);
++arr4;
} while (0);
dbgprt(ZPXHOWCHR,"encode_wrap: EXIT ctl_wraplen=%d ctl_wrapnow=%d\n",
ctl->ctl_wraplen,ctl->ctl_wrapnow);
return arr4;
}
// encode_putx -- perform encoding
static inline_nodebug byte *
encode_putx(b64ctl_t *ctl,byte *arr4,const byte *arr3,int padlen)
{
dbgprt(ZPXHOWCHR,"b64encode: LOOP arr3=(" FMT " " FMT " " FMT ") padlen=%d\n",
PRT(arr3[0]),PRT(arr3[1]),PRT(arr3[2]),padlen);
PUTE(0,(arr3[0] & 0xfc) >> 2);
PUTE(1,((arr3[0] & 0x03) << 4) + ((arr3[1] & 0xf0) >> 4));
PUTE(2,((arr3[1] & 0x0f) << 2) + ((arr3[2] & 0xc0) >> 6));
PUTE(3,arr3[2] & 0x3f);
switch (padlen) {
case 2:
PUT4(2,'=');
// fall through
case 1:
PUT4(3,'=');
break;
}
encode_xcheck(ctl,arr4,4,arr3,3,padlen);
arr4 += 4;
return arr4;
}
// is_base64 -- ensure char is a valid base64 encoding char
#if ! B64DECODE_FAST
static inline int
is_base64(byte c)
{
return (isalnum(c) || (c == '+') || (c == '/'));
}
#endif
// decode_find -- convert encoded base64 char into binary byte
static inline byte
decode_find(byte chr)
{
byte out;
#if B64DECODE_FAST
out = decode_fast[chr];
#else
const char *find = strchr(base64_chars,chr);
out = find - base64_chars;
#endif
dbgprt(ZPXHOWCHR,"decode_find: XLAT out=" FMT " chr=" FMT "\n",
PRT(out),PRT(chr));
if (out == 0xFF)
sysfault("b64decode: NONBASE64 chr=" FMT "\n",PRT(chr));
return out;
}
// decode_put -- convert 4 byte encoded base64 cell into 3 binary bytes
static inline_nodebug byte *
decode_put(byte *arr3,byte *arr4,int a4len)
{
int idx;
int a3len;
dbgprt(ZPXHOWCHR,"decode_put: ENTER a4len=%d\n",a4len);
// convert to binary codes
idx = 0;
for (; idx < a4len; ++idx)
arr4[idx] = decode_find(arr4[idx]);
// add zero padding
#if 0
for (; idx < 4; ++idx)
arr4[idx] = 0;
#endif
a3len = (a4len * 3) / 4;
// store 3 binary chars for 4 byte cell
arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
arr3[1] = ((arr4[1] & 0xf) << 4) + ((arr4[2] & 0x3c) >> 2);
arr3[2] = ((arr4[2] & 0x3) << 6) + arr4[3];
#if B64VERBOSE
for (idx = 0; idx < a3len; ++idx)
dbgprt(ZPXHOWCHR,"decode_put: arr3[%d]=" FMT "\n",PRT(arr3[idx]));
#endif
// advance output pointer
arr3 += a3len;
dbgprt(ZPXHOWCHR,"decode_put: EXIT a3len=%d\n",a3len);
return arr3;
}
// finish -- finish an operation
static size_t
finish(b64ctl_t *ctl,byte *out)
{
b64buf_t *buf = &ctl->ctl_out;
size_t outlen = OUTLEN(buf,out);
if (outlen > buf->buf_len)
sysfault("finish: buffer overflow -- buf_len=%zu outlen=%zu\n",
buf->buf_len,outlen);
buf->buf_len = outlen;
return outlen;
}
// b64encode -- encode raw data to base64
// RETURNS actual output length
size_t
b64encode(b64ctl_t *ctl)
{
const byte *inb = ctl->ctl_inb.buf_base;
ssize_t inblen = ctl->ctl_inb.buf_len;
byte *arr4 = ctl->ctl_out.buf_base;
int anyflg = (inblen > 0);
dbgprt(ZPXHOWB64,"b64encode: ENTER inb=%p inblen=%zu\n",inb,inblen);
for (; inblen >= 3; inblen -= 3, inb += 3) {
arr4 = encode_putx(ctl,arr4,inb,0);
arr4 = encode_wrap(ctl,arr4);
}
if (inblen) {
byte arr3[3];
u32 j = 0;
int padlen = 3 - inblen;
dbgprt(ZPXHOWB64,"b64encode: POST inblen=%zu padlen=%d\n",
inblen,padlen);
// copy over valid bytes
for (; inblen > 0; --inblen, ++j)
arr3[j] = inb[j];
// add zeroes as padding?
for (; j < 3; ++j)
arr3[j] = 0;
// add final padding
arr4 = encode_putx(ctl,arr4,arr3,padlen);
arr4 = encode_wrap(ctl,arr4);
}
// sequence should end with newline
do {
dbgprt(ZPXHOWB64,"b64encode: WRAP anyflg=%d ctl_wrapnow=%d\n",
anyflg,ctl->ctl_wrapnow);
// no newline generation
if (ctl->ctl_wrapmax <= 0)
break;
// no legit data
if (! anyflg)
break;
// don't double up newlines if we just did a newline
if (ctl->ctl_wrapnow)
break;
PUT4(0,'\n');
encode_xcheck(ctl,arr4,1,NULL,0,0);
++arr4;
} while (0);
// always add EOS
#if 0
*arr4 = 0;
#endif
size_t outlen = finish(ctl,arr4);
dbgprt(ZPXHOWB64,"b64encode: EXIT outlen=%zu\n",outlen);
return outlen;
}
// b64decode -- decode base64 into raw binary
// RETURNS: actual output length
size_t
b64decode(b64ctl_t *ctl)
{
const char *inb = (const char *) ctl->ctl_inb.buf_base;
size_t inblen = ctl->ctl_inb.buf_len;
size_t inbidx = 0;
byte *arr3 = ctl->ctl_out.buf_base;
int outidx = 0;
byte arr4[4];
dbgprt(ZPXHOWB64,"b64decode: ENTER inb=%p\n",inb);
for (inbidx = 0; inbidx < inblen; ++inbidx) {
int chr = inb[inbidx];
dbgprt(ZPXHOWCHR,"b64decode: LOOP chr=" FMT "\n",PRT(chr));
// stop when we hit a pad char
if (chr == '=')
break;
// ignore newlines
if (chr == '\n')
continue;
// not a valid char
#if ! B64DECODE_FAST
if (! is_base64(chr)) {
sysfault("b64decode: NONBASE64 chr=" FMT "\n",PRT(chr));
break;
}
#endif
arr4[outidx++] = chr;
if (outidx == 4) {
arr3 = decode_put(arr3,arr4,4);
outidx = 0;
}
}
if (outidx > 0) {
dbgprt(ZPXHOWB64,"b64decode: POST outidx=%d inblen=%zu\n",
outidx,inblen);
// fill remainder with pad/zeroes
for (inbidx = outidx; inbidx < 4; ++inbidx)
arr4[inbidx] = 0;
arr3 = decode_put(arr3,arr4,outidx);
}
size_t outlen = finish(ctl,arr3);
dbgprt(ZPXHOWB64,"b64decode: EXIT outlen=%zu\n",outlen);
return outlen;
}
// b64bufdup -- safe buffer duplicate
void
b64bufdup(b64buf_t *dst,const b64buf_t *src)
{
const char *tag = dst->buf_tag;
dbgprt(ZPXHOWB64,"b64bufdup: ENTER dst=%s/%p src=%s/%p\n",
dst->buf_tag,dst->buf_base,src->buf_tag,src->buf_base);
if (dst->buf_base != NULL)
sysfault("b64bufdup: non-null -- dst=%s buf_base=%p src=%s\n",
tag,dst->buf_base,src->buf_tag);
*dst = *src;
dst->buf_tag = tag;
dbgprt(ZPXHOWB64,"b64bufdup: EXIT\n");
}
// b64bufput -- output buffer to file
void
b64bufput(const b64buf_t *bufp,const char *ofile)
{
int fd;
b64buf_t buf_;
b64buf_t *buf = &buf_;
ssize_t xlen;
*buf = *bufp;
printf("b64bufput: %s buf_base=%p buf_len=%zu\n",
ofile,buf->buf_base,buf->buf_len);
do {
if (buf->buf_opt & B64MMAP)
break;
fd = open(ofile,O_WRONLY | O_TRUNC | O_CREAT,0644);
if (fd < 0)
sysfault("b64bufput: unable to open '%s' -- %s\n",
ofile,strerror(errno));
for (; buf->buf_len > 0;
buf->buf_len -= xlen, buf->buf_base += xlen) {
xlen = write(fd,buf->buf_base,buf->buf_len);
if (xlen < 0)
sysfault("bufput: write error -- %s\n",strerror(errno));
}
CLOSEME(fd);
} while (0);
}
// b64bufget -- input file to buffer
void
b64bufget(b64buf_t *buf,const char *ifile)
{
struct stat st;
size_t cap;
int fd;
const char *etag = NULL;
ssize_t xlen;
do {
fd = open(ifile,O_RDONLY);
if (fd < 0) {
etag = "open";
break;
}
if (buf->buf_opt & B64MMAP) {
buf->buf_fdmap = fd;
// get file size
if (fstat(fd,&st) < 0) {
etag = "fstat";
break;
}
buf->buf_len = st.st_size;
buf->buf_base = mmap(NULL,buf->buf_len,PROT_READ,
MAP_SHARED,buf->buf_fdmap,0);
if (buf->buf_base == MAP_FAILED) {
etag = "mmap";
break;
}
break;
}
cap = 0;
buf->buf_len = 0;
buf->buf_base = NULL;
while (1) {
if (buf->buf_len >= cap) {
cap += 4096;
buf->buf_base = realloc(buf->buf_base,cap + 1);
if (buf->buf_base == NULL) {
etag = "realloc";
break;
}
}
xlen = read(fd,&buf->buf_base[buf->buf_len],cap - buf->buf_len);
if (xlen < 0) {
etag = "read";
break;
}
if (xlen == 0)
break;
buf->buf_len += xlen;
}
} while (0);
if (etag != NULL)
sysfault("b64bufget: unable to %s '%s' -- %s\n",
etag,ifile,strerror(errno));
do {
if (buf->buf_opt & B64MMAP)
break;
CLOSEME(buf->buf_fdmap);
cap = buf->buf_len;
buf->buf_base[cap] = 0;
buf->buf_base = realloc(buf->buf_base,cap + 1);
if (buf->buf_base == NULL)
sysfault("b64bufget: realloc fail -- %s\n",strerror(errno));
} while (0);
printf("b64bufget: %s buf_base=%p buf_len=%zu\n",
ifile,buf->buf_base,buf->buf_len);
}
// b64setup -- passive setup
void
b64setup(b64ctl_t *ctl,u32 opt,const char *tag)
{
b64buf_t *buf;
memset(ctl,0,sizeof(*ctl));
ctl->ctl_tag = tag;
buf = &ctl->ctl_out;
buf->buf_fdmap = -1;
buf->buf_opt = opt;
buf->buf_tag = "ctl_out";
buf = &ctl->ctl_inb;
buf->buf_fdmap = -1;
buf->buf_opt = opt;
buf->buf_tag = "ctl_inb";
ctl->ctl_fdchk = -1;
}
// init_outbuf -- initialize output buffer
static int
init_outbuf(b64ctl_t *ctl,size_t outlen)
{
b64buf_t *buf = &ctl->ctl_out;
int err = -1;
dbgprt(ZPXHOWB64,"init_outbuf: ENTER ctl=%s buf=%s buf_base=%p buf_len=%zu outlen=%zu\n",
ctl->ctl_tag,buf->buf_tag,buf->buf_base,buf->buf_len,outlen);
buf->buf_len = outlen;
do {
if (! (buf->buf_opt & B64MMAP)) {
buf->buf_base = realloc(buf->buf_base,outlen);
if (buf->buf_base == NULL)
break;
err = 0;
break;
}
if (ftruncate(buf->buf_fdmap,outlen) < 0)
break;
buf->buf_base = mmap(NULL,outlen,PROT_READ | PROT_WRITE,
MAP_SHARED,buf->buf_fdmap,0);
if (buf->buf_base == MAP_FAILED) {
buf->buf_base = NULL;
break;
}
err = 0;
} while (0);
dbgprt(ZPXHOWB64,"init_outbuf: EXIT err=%d\n",err);
return err;
}
// b64init_encode -- initialize for encode
// RETURNS: 0=okay, -1=error
int
b64init_encode(b64ctl_t *ctl,const b64buf_t *inb,int wrapmax)
{
size_t outlen;
// copy in the buffer
b64bufdup(&ctl->ctl_inb,inb);
// we need for 4 bytes of output for each 3 bytes of input
outlen = inb->buf_len;
outlen *= 4;
outlen /= 3;
outlen += 4;
// space for padding
outlen += 4;
// we wrap with newline every N bytes
if (wrapmax == 0)
wrapmax = 76;
ctl->ctl_wrapmax = wrapmax;
if (wrapmax > 0)
outlen += (outlen + wrapmax) / wrapmax;
return init_outbuf(ctl,outlen);
}
#if B64DECODE_FAST
// init_fast -- initialize fast decode lookup
static void
init_fast(void)
{
const char *cp = base64_chars;
memset(decode_fast,0xFF,sizeof(decode_fast));
for (int chr = *cp; chr != 0; ++cp, chr = *cp) {
size_t off = cp - base64_chars;
decode_fast[chr] = off;
dbgprt(ZPXHOWB64,"init_fast: FASTINIT chr='%c' off=%zu\n",
chr,off);
}
}
#endif
// b64init_decode -- initialize for decode
// RETURNS: 0=okay, -1=error
int
b64init_decode(b64ctl_t *ctl,b64buf_t *inb)
{
size_t outlen;
int err;
dbgprt(ZPXHOWB64,"b64init_decode: ENTER buf_len=%zu\n",inb->buf_len);
#if B64DECODE_FAST
if (decode_fast['B'] == 0)
init_fast();
#endif
if (inb != &ctl->ctl_inb)
ctl->ctl_inb = *inb;
inb = &ctl->ctl_inb;
// caller may already know the input length, but ...
#if 0
if (inb->buf_len == 0) {
inb->buf_len = strlen(inb->buf_base);
dbgprt(ZPXHOWB64,"b64init_decode: STRLEN buf_len=%zu\n",inb->buf_len);
}
#endif
// we need for 3 bytes of output for each 4 bytes of input
outlen = inb->buf_len;
outlen *= 3;
outlen += 3;
outlen /= 4;
err = init_outbuf(ctl,outlen);
dbgprt(ZPXHOWB64,"b64init_decode: EXIT err=%d outlen=%zu\n",err,outlen);
return err;
}
// b64destroy -- destroy/deinit output buffer
void
b64destroy(b64ctl_t *ctl)
{
b64bufrls(&ctl->ctl_out,"ctl_out");
b64bufrls(&ctl->ctl_inb,"ctl_inb");
}
// b64bufrls -- destroy/deinit output buffer
void
b64bufrls(b64buf_t *buf,const char *who)
{
size_t outlen = buf->buf_len;
int err = -1;
dbgprt(ZPXHOWB64,"b64bufrls: ENTER buf_opt=%8.8X buf_base=%p outlen=%zu (from %s)\n",
buf->buf_opt,buf->buf_base,outlen,who);
do {
if (! (buf->buf_opt & B64MMAP)) {
FREEME(buf->buf_base);
err = 0;
break;
}
if (munmap(buf->buf_base,outlen) < 0)
break;
if (buf->buf_opt & B64TRIM) {
if (ftruncate(buf->buf_fdmap,outlen) < 0)
break;
}
CLOSEME(buf->buf_fdmap);
} while (0);
if (err < 0)
exit(1);
buf->buf_base = NULL;
buf->buf_len = 0;
dbgprt(ZPXHOWB64,"b64bufrls: EXIT\n");
}
FILE: b64test.c
// b64test.c -- test program for base64
#include <base64.h>
int opt_a; // exhaustive test
int opt_L; // binary file length
int opt_k; // keep temp files
int opt_m; // use mmap
u32 opt_R = 1; // random seed
int opt_T; // number of tests
int opt_W; // wrap length
int tstno = 1; // current test number
#define ALLFILES(_cmd) \
_cmd(bin) \
_cmd(encexp) \
_cmd(encact) \
_cmd(dcdexp) \
_cmd(dcdact)
#define FILEDEF(_pre) \
const char *_pre##_file = #_pre ".TMP";
ALLFILES(FILEDEF)
#define FILERM(_pre) \
unlink(_pre##_file);
// initial random binary buffer
B64BUFDEF(binbuf);
// encoding control
B64BUFDEF(encbuf);
B64CTLDEF(ctlenc);
// decoding control
B64BUFDEF(dcdbuf);
B64CTLDEF(ctldcd);
int
doexec(const char *cmd)
{
printf("DOEXEC: %s\n",cmd);
int ignflg = (cmd[0] == '!');
if (ignflg)
++cmd;
int code = system(cmd);
if (code && (! ignflg))
exit(1);
return code;
}
int
xrand(void)
{
return rand_r(&opt_R);
}
// doclean -- remove temp files
void
doclean(void)
{
do {
if (opt_k)
break;
ALLFILES(FILERM);
} while (0);
}
void
dotest(int binlen)
{
int err;
u32 opt = 0;
b64ctl_t *ctl;
b64buf_t *buf;
char cmd[1000];
printf("\n");
for (int col = 1; col <= 80; ++col)
putchar('-');
printf("\n");
dbgprt(ZPXHOWB64,"dotest: ENTER binlen=%d\n",binlen);
buf = &binbuf;
if (binlen <= 0)
buf->buf_len = -binlen;
else
buf->buf_len = xrand() % binlen;
printf("dotest: %d -- R=%u L=%8.8zX/%zu\n",
tstno,opt_R,buf->buf_len,buf->buf_len);
doclean();
if (opt_m) {
opt |= B64MMAP;
opt |= B64TRIM;
}
// NOTE: this gets freed in the b64destroy for ctlenc below
buf->buf_base = NULL;
buf->buf_base = realloc(buf->buf_base,buf->buf_len);
if (buf->buf_base == NULL)
sysfault("dotest: realloc failure -- %s\n",strerror(errno));
// get a random buffer
for (size_t bufidx = 0; bufidx < buf->buf_len; ++bufidx)
buf->buf_base[bufidx] = xrand();
// dump it for base64 program
b64bufput(buf,bin_file);
// have base64 encode it
sprintf(cmd,"base64 %s > %s",bin_file,encexp_file);
doexec(cmd);
ctl = &ctlenc;
b64setup(ctl,opt,"ctlenc");
err = b64init_encode(ctl,buf,opt_W);
if (err < 0)
sysfault("dotest: b64init_encode failure -- %s\n",strerror(errno));
#if B64ENCODE_XCHECK
ctl->ctl_fdchk = open(encexp_file,O_RDONLY);
#endif
// have our routine encode it
b64encode(ctl);
#if B64ENCODE_XCHECK
CLOSEME(ctl->ctl_fdchk);
#endif
// save it to a file
b64bufput(&ctl->ctl_out,encact_file);
b64destroy(ctl);
// compare them
sprintf(cmd,"diff -u %s %s",encexp_file,encact_file);
doexec(cmd);
// decode it
sprintf(cmd,"base64 -d %s > %s",encexp_file,dcdexp_file);
doexec(cmd);
// passive setup
ctl = &ctldcd;
b64setup(ctl,opt,"ctldcd");
// load up the encoded file into the decode input buffer
b64bufget(&dcdbuf,encact_file);
// decode the data
err = b64init_decode(ctl,&dcdbuf);
if (err < 0)
sysfault("dotest: b64init_decode failure -- %s\n",strerror(errno));
// have our routine decode it
b64decode(ctl);
b64bufput(&ctl->ctl_out,dcdact_file);
b64destroy(ctl);
// compare them
sprintf(cmd,"cmp %s %s",dcdexp_file,dcdact_file);
doexec(cmd);
// final temp file cleanup
doclean();
printf("dotest: complete\n");
dbgprt(ZPXHOWB64,"dotest: EXIT\n");
}
int
main(int argc,char **argv)
{
char *cp;
--argc;
++argv;
setlinebuf(stdout);
// use a special output directory
do {
cp = getenv("GENDIR");
if (cp == NULL)
break;
printf("GENDIR = %s\n",cp);
if (chdir(cp) < 0)
sysfault("main: chdir fault -- %s\n",strerror(errno));
} while (0);
// parse options
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'a':
opt_a = (*cp != 0) ? atoi(cp) : 1;
break;
case 'L':
opt_L = (*cp != 0) ? atoi(cp) : 1000;
break;
case 'k':
opt_k = ! opt_k;
break;
case 'm':
opt_m = ! opt_m;
break;
case 'R':
opt_R = (*cp != 0) ? atoi(cp) : 1;
break;
case 'T':
opt_T = (*cp != 0) ? atoi(cp) : 100;
break;
case 'W':
opt_W = atoi(cp);
break;
}
}
// ensure at least one test
if (opt_T <= 0)
opt_T = 1;
do {
// ensure we have a non-zero length
if (opt_L == 0)
opt_L = 100;
// do random testing
if (opt_a < 0) {
for (; tstno <= opt_T; ++tstno)
dotest(opt_L);
break;
}
// get absolute value
if (opt_L < 0)
opt_L = -opt_L;
// ensure we get at least one test
if (opt_L < opt_a)
opt_L = opt_a;
// do a range of explicit lengths
for (int binlen = opt_a; binlen <= opt_L; ++binlen, ++tstno)
dotest(-binlen);
} while (0);
printf("%d tests completed\n",tstno);
return 0;
}
FILE: dbgcom.h
// dbgflg.h -- base/debug definitions
#ifndef _dbgflg_h_
#define _dbgflg_h_
#include <stdio.h>
#include <stdlib.h>
typedef unsigned char byte;
typedef unsigned int u32;
#define ZPXHOWALL(_cmd) \
_cmd(ZPXHOWB64) \
_cmd(ZPXHOWCHR) \
_cmd(ZPXHOWTST)
#define _DBGENUM(_sym) \
_sym,
enum {
ZPXHOWANY,
ZPXHOWALL(_DBGENUM)
ZPXHOWMAX
};
byte dbgflg[ZPXHOWMAX];
static inline byte
dbgok(int lvl)
{
return dbgflg[lvl];
}
#if DEBUG || _USE_ZPRT_
#define XVERBOSE 1
#define dbgprt(_lvl,_fmt...) \
do { \
if (! dbgok(_lvl)) \
break; \
int sverr = errno; \
printf(_fmt); \
errno = sverr; \
} while (0)
#define inline_nodebug /**/
#else
#define XVERBOSE 0
#define inline_nodebug inline
#define dbgprt(_fmt...) \
do { } while (0)
#endif
#define sysfault(_fmt...) \
do { \
printf(_fmt); \
exit(1); \
} while (0)
#define RNGE(_val,_lo,_hi) \
(((_val) >= (_lo)) && ((_val) <= (_hi)))
#define PRTABLE(_val) \
RNGE(_val,0x20,0x7E) ? (_val) : '.'
#define CLOSEME(_fd) \
do { \
if (_fd < 0) \
break; \
dbgprt(ZPXHOWB64,"CLOSEME: fd=%d (from %d)\n",_fd,__LINE__); \
close(_fd); \
_fd = -1; \
} while (0)
#define FREEME(_ptr) \
do { \
dbgprt(ZPXHOWB64,"FREEME: ptr=%p (from %d)\n",_ptr,__LINE__); \
free(_ptr); \
_ptr = NULL; \
} while (0)
#endif
FILE: dbgcom.c
// dbgcom.c -- tracing setup
#include <dbgcom.h>
struct dbgdef {
int dbg_lvl;
const char *dbg_sym;
};
#define _ZPXDEF(_sym) \
{ .dbg_lvl = _sym, .dbg_sym = #_sym },
struct dbgdef dbgdef[] = {
ZPXHOWALL(_ZPXDEF)
{ .dbg_sym = NULL }
};
void __attribute__((constructor))
dbginit(void)
{
struct dbgdef *dbg = dbgdef;
for (; dbg->dbg_sym != NULL; ++dbg) {
char *cp = getenv(dbg->dbg_sym);
if (cp == NULL)
continue;
if (! atoi(cp))
continue;
dbgflg[dbg->dbg_lvl] = 1;
dbgflg[ZPXHOWANY] = 1;
}
}

How to Combine 2 Struct arrays in C

iv tried a lot of solutions to try to get this working (i.e using memcpy etc) I cant seem to find the issue, depending on what I try I either end up with gibberish or SEGV
iv spent a lot of time already googling and trying different ways, i still cant figure out why the arrays won't combine successfully
#include <stdio.h>
#include <stdint.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <unistd.h>
#define log_info printf
typedef struct
{
char* name;
//size_t size;
} entry_t;
/* qsort struct comparison function (C-string field) */
static int struct_cmp_by_name(const void* a, const void* b)
{
entry_t* ia = (entry_t*)a;
entry_t* ib = (entry_t*)b;
return strcmp(ia->name, ib->name);
/* strcmp functions works exactly as expected from comparison function */
}
entry_t* get_item_entries(const char* dirpath, int* count)
{
struct dirent* dent;
char buffer[512]; // fixed buffer
int dfd = 0,
n, r = 1; // item counter, rounds to loop
entry_t* p = NULL; // we fill this struct with items
loop:
n = 0;
printf("loop: %d, count:%d\n", r, *count);
// try to open dir
dfd = open(dirpath, O_RDONLY, 0);
if (dfd < 0)
{
printf("Invalid directory. (%s)\n", dirpath);
*count = -1;
return NULL;
}
else
{
printf("open(%s)\n", dirpath);
}
memset(buffer, 0, sizeof(buffer));
while (syscall(SYS_getdents, dfd, buffer, sizeof(buffer)) != 0)
{
dent = (struct dirent*)buffer;
while (dent->d_fileno)
{ // skip `.` and `..`
if (!strncmp(dent->d_name, "..", 2)
|| !strncmp(dent->d_name, ".", 1)) goto skip_dent;
// deal with filtering outside of this function, we just skip .., .
switch (r)
{ // first round: just count items
case 1:
{
// skip special cases
if (dent->d_fileno == 0) goto skip_dent;
break;
}
// second round: store filenames
case 0: p[n].name = strdup(dent->d_name); break;
}
n++;
skip_dent:
dent = (struct dirent*)((void*)dent + dent->d_reclen);
if (dent == (void*)&buffer[512]) break; // refill buffer
}
memset(buffer, 0, sizeof(buffer));
}
close(dfd);
// on first round, calloc for our list
if (!p)
{ // now n holds total item count, note it
p = calloc(n, sizeof(entry_t));
*count = n;
}
// first round passed, loop
r--; if (!r) goto loop;
// report count
printf("%d items at %p, from 1-%d\n", *count, (void*)p, *count);
/* resort using custom comparision function */
qsort(p, *count, sizeof(entry_t), struct_cmp_by_name);
// report items
//for (int i = 0; i < num; ++i) log_error( "%s", p[i].name);
return p;
}
int main(int argc, char* argv[])
{
int HDD_count = -1;
uint32_t total = -1;
int ext_count = -1;
entry_t* e = NULL;
entry_t *HDD = get_item_entries("/mnt/f/n", &HDD_count);
entry_t* ext = get_item_entries("/mnt/f/dls", &ext_count);
total = ext_count + HDD_count;
e = (entry_t*)malloc(sizeof *e * total);
if (e != NULL)
{
for (int i = 1; i < HDD_count; i++)
{
log_info("HDD[%i].name %s\n", i, HDD[i].name);
e[i].name = strdup(HDD[i].name);
}
for (int i = 1; i < ext_count; i++)
{
log_info("ext[%i].name %s\n", i, ext[i].name);
e[i + HDD_count].name = strdup(ext[i].name);
}
}
else
printf("Failed to Allocate the Array");
char tmp[256];
int i = 1, j;
for(j = 1; j <= total; j++)
{
snprintf(&tmp[0], 255, "%s", e[ j].name);
log_info("%i:%s\n", j , tmp);
}
return 0;
}
Here is a rewrite of a snippet of main() that I mentioned in my comment above:
#define CHECK(p, msg) if(!(p)) { printf("%s:%d: %s", __FILE__, __LINE__, msg); return 1;}
...
entry_t *HDD = get_item_entries("/mnt/f/n", &HDD_count);
CHECK(HDD, "HDD failed to get entries");
entry_t *ext = get_item_entries("/mnt/f/dls", &ext_count);
CHECK(ext, "ext failed to get entries");
uint32_t total = HDD_count + ext_count;
entry_t *e = malloc(total * sizeof(*e));
CHECK(e, "malloc failed");
for(int i = 0; i < HDD_count; i++) {
log_info("HDD[%i].name %s\n", i, HDD[i].name);
e[i].name = strdup(HDD[i].name);
}
// write a function instead of duplicating code?
for (int i = 0; i < ext_count; i++) {
log_info("ext[%i].name %s\n", i, ext[i].name);
e[HDD_count + i].name = strdup(ext[i].name);
}
It looks like a short lived program, but I would still free the values from strdup() and e itself.

Segmentation fault while comparing elements in a dynamically allocated array

This program tries to simulate FIFO and LRU page replacement. I am trying to implement a simple queue using a dynamically allocated array for the FIFO queue. I want the "page" to be stored in the array.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/*
* Some compile-time constants.
*/
#define REPLACE_NONE 0
#define REPLACE_FIFO 1
#define REPLACE_LRU 2
#define REPLACE_SECONDCHANCE 3
#define REPLACE_OPTIMAL 4
#define TRUE 1
#define FALSE 0
#define PROGRESS_BAR_WIDTH 60
#define MAX_LINE_LEN 100
/*
* Some function prototypes to keep the compiler happy.
*/
int setup(void);
int teardown(void);
int output_report(void);
long resolve_address(long, int);
void error_resolve_address(long, int);
/*
* Variables used to keep track of the number of memory-system events
* that are simulated.
*/
int page_faults = 0;
int mem_refs = 0;
int swap_outs = 0;
int swap_ins = 0;
/*
* Page-table information. You may want to modify this in order to
* implement schemes such as SECONDCHANCE. However, you are not required
* to do so.
*/
struct page_table_entry *page_table = NULL;
struct page_table_entry {
long page_num;
int dirty;
int free;
};
/*
* These global variables will be set in the main() function. The default
* values here are non-sensical, but it is safer to zero out a variable
* rather than trust to random data that might be stored in it -- this
* helps with debugging (i.e., eliminates a possible source of randomness
* in misbehaving programs).
*/
int size_of_frame = 0; /* power of 2 */
int size_of_memory = 0; /* number of frames */
int page_replacement_scheme = REPLACE_NONE;
long *queue;
void add_end(long page_num){
for(int i=0; i<size_of_memory; i++){
if(queue[i] == NULL){
queue[i] = page_num;
break;
}
}
}
long peek_front(){
return queue[0];
}
void remove_front(){
for(int i=0; i<size_of_memory; i++){
queue[i] = queue[i+1];
}
}
// typedef struct page_in_queue page_in_queue;
// struct page_in_queue {
// long page_num;
// page_in_queue *next;
// };
// page_in_queue *new_page(){
// page_in_queue *new_page;
// new_page = (page_in_queue *) malloc(sizeof(page_in_queue));
// new_page->next = NULL;
// return new_page;
// }
// page_in_queue *add_end(page_in_queue *queue, page_in_queue *page){
// page_in_queue *curr;
// if (queue == NULL) {
// page->next = NULL;
// return page;
// }
// for (curr = queue; curr->next != NULL; curr = curr->next);
// curr->next = page;
// page->next = NULL;
// return queue;
// }
// page_in_queue *peek_front(page_in_queue *queue) {
// return queue;
// }
// page_in_queue *remove_front(page_in_queue *queue){
// if (queue == NULL) {
// return NULL;
// }
// page_in_queue *new_front_page = queue->next;
// free(queue);
// return new_front_page;
// }
long *list;
void add(long page_num){
int i;
for (i=0; i<size_of_memory; i++){
list[i] = list[i+1];
}
list[i] = page_num;
}
long peek_least_used(){
return list[0];
}
/*
* Function to convert a logical address into its corresponding
* physical address. The value returned by this function is the
* physical address (or -1 if no physical address can exist for
* the logical address given the current page-allocation state.
*/
long resolve_address(long logical, int memwrite)
{
int i;
long page, frame;
long offset;
long mask = 0;
long effective;
/* Get the page and offset */
page = (logical >> size_of_frame);
for (i=0; i<size_of_frame; i++) {
mask = mask << 1;
mask |= 1;
}
offset = logical & mask;
if (page_replacement_scheme == 2){
add(page);
}
/* Find page in the inverted page table. */
frame = -1;
for ( i = 0; i < size_of_memory; i++ ) {
if (!page_table[i].free && page_table[i].page_num == page) {
frame = i;
break;
}
}
/* If frame is not -1, then we can successfully resolve the
* address and return the result. */
if (frame != -1) {
effective = (frame << size_of_frame) | offset;
return effective;
}
/* If we reach this point, there was a page fault. Find
* a free frame. */
page_faults++;
for ( i = 0; i < size_of_memory; i++) {
if (page_table[i].free) {
frame = i;
break;
}
}
// page_in_queue *temp_page;
// page_in_queue *queue;
long rem_page;
/* If we found a free frame, then patch up the
* page table entry and compute the effective
* address. Otherwise return -1.
*/
if (frame != -1) {
page_table[frame].page_num = page;
page_table[i].free = FALSE;
swap_ins++;
if (page_replacement_scheme == 1){
// temp_page = new_page();
// temp_page->page_num = page;
add_end(page);
}
effective = (frame << size_of_frame) | offset;
return effective;
}
else {
if (page_replacement_scheme == 1){
rem_page = peek_front();
for ( i = 0; i < size_of_memory; i++){
if(page_table[i].page_num == rem_page){
page_table[i].page_num = page;
page_table[i].free = FALSE;
page_table[i].dirty = memwrite;
swap_ins++;
if(page_table[i].dirty == 1){
swap_outs++;
}
frame = i;
break;
}
}
remove_front();
effective = (frame << size_of_frame) | offset;
return effective;
}
if (page_replacement_scheme == 2){
long temp = peek_least_used();
for ( i = 0; i < size_of_memory; i++){
if(page_table[i].page_num == temp){
page_table[i].page_num = page;
page_table[i].free = FALSE;
page_table[i].dirty = memwrite;
swap_ins++;
if(page_table[i].dirty == 1){
swap_outs++;
}
frame = i;
break;
}
}
effective = (frame << size_of_frame) | offset;
return effective;
}
if (page_replacement_scheme == 3){
}
}
}
/*
* Super-simple progress bar.
*/
void display_progress(int percent)
{
int to_date = PROGRESS_BAR_WIDTH * percent / 100;
static int last_to_date = 0;
int i;
if (last_to_date < to_date) {
last_to_date = to_date;
} else {
return;
}
printf("Progress [");
for (i=0; i<to_date; i++) {
printf(".");
}
for (; i<PROGRESS_BAR_WIDTH; i++) {
printf(" ");
}
printf("] %3d%%", percent);
printf("\r");
fflush(stdout);
}
int setup()
{
int i;
page_table = (struct page_table_entry *)malloc(
sizeof(struct page_table_entry) * size_of_memory
);
if (page_table == NULL) {
fprintf(stderr,
"Simulator error: cannot allocate memory for page table.\n");
exit(1);
}
for (i=0; i<size_of_memory; i++) {
page_table[i].free = TRUE;
}
return -1;
}
int teardown()
{
return -1;
}
void error_resolve_address(long a, int l)
{
fprintf(stderr, "\n");
fprintf(stderr,
"Simulator error: cannot resolve address 0x%lx at line %d\n",
a, l
);
exit(1);
}
int output_report()
{
printf("\n");
printf("Memory references: %d\n", mem_refs);
printf("Page faults: %d\n", page_faults);
printf("Swap ins: %d\n", swap_ins);
printf("Swap outs: %d\n", swap_outs);
return -1;
}
int main(int argc, char **argv)
{
/* For working with command-line arguments. */
int i;
char *s;
/* For working with input file. */
FILE *infile = NULL;
char *infile_name = NULL;
struct stat infile_stat;
int line_num = 0;
int infile_size = 0;
/* For processing each individual line in the input file. */
char buffer[MAX_LINE_LEN];
long addr;
char addr_type;
int is_write;
/* For making visible the work being done by the simulator. */
int show_progress = FALSE;
/* Process the command-line parameters. Note that the
* REPLACE_OPTIMAL scheme is not required for A#3.
*/
for (i=1; i < argc; i++) {
if (strncmp(argv[i], "--replace=", 9) == 0) {
s = strstr(argv[i], "=") + 1;
if (strcmp(s, "fifo") == 0) {
page_replacement_scheme = REPLACE_FIFO;
} else if (strcmp(s, "lru") == 0) {
page_replacement_scheme = REPLACE_LRU;
} else if (strcmp(s, "secondchance") == 0) {
page_replacement_scheme = REPLACE_SECONDCHANCE;
} else if (strcmp(s, "optimal") == 0) {
page_replacement_scheme = REPLACE_OPTIMAL;
} else {
page_replacement_scheme = REPLACE_NONE;
}
} else if (strncmp(argv[i], "--file=", 7) == 0) {
infile_name = strstr(argv[i], "=") + 1;
} else if (strncmp(argv[i], "--framesize=", 12) == 0) {
s = strstr(argv[i], "=") + 1;
size_of_frame = atoi(s);
} else if (strncmp(argv[i], "--numframes=", 12) == 0) {
s = strstr(argv[i], "=") + 1;
size_of_memory = atoi(s);
if (page_replacement_scheme == 1){
queue = (long *)malloc(sizeof(long)*size_of_memory);
}
if (page_replacement_scheme == 2){
list = (long *)malloc(sizeof(long)*size_of_memory);
}
} else if (strcmp(argv[i], "--progress") == 0) {
show_progress = TRUE;
}
}
if (infile_name == NULL) {
infile = stdin;
} else if (stat(infile_name, &infile_stat) == 0) {
infile_size = (int)(infile_stat.st_size);
/* If this fails, infile will be null */
infile = fopen(infile_name, "r");
}
if (page_replacement_scheme == REPLACE_NONE ||
size_of_frame <= 0 ||
size_of_memory <= 0 ||
infile == NULL)
{
fprintf(stderr,
"usage: %s --framesize=<m> --numframes=<n>", argv[0]);
fprintf(stderr,
" --replace={fifo|lru|optimal} [--file=<filename>]\n");
exit(1);
}
setup();
while (fgets(buffer, MAX_LINE_LEN-1, infile)) {
line_num++;
if (strstr(buffer, ":")) {
sscanf(buffer, "%c: %lx", &addr_type, &addr);
if (addr_type == 'W') {
is_write = TRUE;
} else {
is_write = FALSE;
}
if (resolve_address(addr, is_write) == -1) {
error_resolve_address(addr, line_num);
}
mem_refs++;
}
if (show_progress) {
display_progress(ftell(infile) * 100 / infile_size);
}
}
teardown();
output_report();
fclose(infile);
exit(0);
}
The file is saved as virtmem.c. This is the makefile:
#
# "makefile" for the virtual-memory simulation.
#
CC=gcc
CFLAGS=-c -Wall -g
all: virtmem
virtmem.o: virtmem.c
$(CC) $(CFLAGS) virtmem.c
virtmem: virtmem.o
$(CC) virtmem.o -o virtmem
clean:
rm -rf *.o virtmem
After running the "make" command, I run the executable with these inputs
./virtmem --framesize=12 --numframes=100 --replace=fifo --file=traces/ls_out.txt --progress
But it is giving a segmentation fault at the conditional "if(queue[i] == NULL)", saying the memory location cannot be accessed. The gdb output is as follows:
Program received signal SIGSEGV, Segmentation fault.
0x0000555555554bea in add_end (page_num=34158723704) at virtmem.c:80
80 if(queue[i] == (long)0){
(gdb) print queue[i]
Cannot access memory at address 0x0
(gdb)

Writing improper number of frames using PortAudio?

Running my program, I appear to not be writing the correct amount of frames according to the index.
$ ./test
Now recording!! Please speak into the microphone.
index = 0
Writing to: test.flac
audio.h:
#include <stdint.h>
#include <string.h>
typedef struct
{
uint32_t duration;
uint16_t format_type;
uint16_t number_of_channels;
uint32_t sample_rate;
uint32_t frameIndex; /* Index into sample array. */
uint32_t maxFrameIndex;
char* recordedSamples;
} AudioData;
int recordFLAC(AudioData* data, const char *fileName);
AudioData* initAudioData(uint32_t sample_rate, uint16_t channels, uint32_t duration);
capture.c:
#include <stdio.h>
#include <stdlib.h>
#include <portaudio.h>
#include <sndfile.h>
#include "audio.h"
AudioData* initAudioData(uint32_t sample_rate, uint16_t channels, uint32_t duration)
{
AudioData* data = malloc(sizeof(*data));
if (!data) return NULL;
data->duration = duration;
data->format_type = 1;
data->number_of_channels = channels;
data->sample_rate = sample_rate;
data->frameIndex = 0;
data->maxFrameIndex = sample_rate * duration;
data->recordedSamples = malloc(sizeof(data->maxFrameIndex));
if(!data->maxFrameIndex) return NULL;
return data;
}
static int recordCallback(const void *inputBuffer, void *outputBuffer, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
AudioData* data = (AudioData*)userData;
const char* buffer_ptr = (const char*)inputBuffer;
char* index_ptr = &data->recordedSamples[data->frameIndex];
(void) outputBuffer;
(void) timeInfo;
(void) statusFlags;
long framesToCalc;
long i;
int finished;
unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
if(framesLeft < frameCount){
framesToCalc = framesLeft;
finished = paComplete;
}else{
framesToCalc = frameCount;
finished = paContinue;
}
if(!inputBuffer){
for(i = 0; i < framesToCalc; i++){
*index_ptr++ = 0;
}
}else{
for(i = 0; i < framesToCalc; i++){
*index_ptr++ = *buffer_ptr++;
}
}
data->frameIndex += framesToCalc;
return finished;
}
int recordFLAC(AudioData* data, const char *fileName)
{
PaStreamParameters inputParameters;
PaStream* stream;
int err = 0;
int totalFrames = data->maxFrameIndex;
int numSamples;
int numBytes;
char max, val;
double average;
numSamples = totalFrames * data->number_of_channels;
numBytes = numSamples;
data->recordedSamples = malloc(numBytes);
if(!data->recordedSamples)
{
printf("Could not allocate record array.\n");
goto done;
}
for(int i = 0; i < numSamples; i++) data->recordedSamples[i] = 0;
if((err = Pa_Initialize())) goto done;
inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
if (inputParameters.device == paNoDevice) {
fprintf(stderr,"Error: No default input device.\n");
goto done;
}
inputParameters.channelCount = data->number_of_channels; /* stereo input */
inputParameters.sampleFormat = data->format_type;
inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
/* Record some audio. -------------------------------------------- */
err = Pa_OpenStream(&stream, &inputParameters, NULL, data->sample_rate, paFramesPerBufferUnspecified, paClipOff, recordCallback, &data);
if(err) goto done;
if((err = Pa_StartStream(stream))) goto done;
puts("Now recording!! Please speak into the microphone.");
while((err = Pa_IsStreamActive(stream)) == 1)
{
Pa_Sleep(1000);
printf("index = %d\n", data->frameIndex);
}
if( err < 0 ) goto done;
err = Pa_CloseStream(stream);
if(err) goto done;
/* Measure maximum peak amplitude. */
max = 0;
average = 0.0;
for(int i = 0; i < numSamples; i++)
{
val = data->recordedSamples[i];
val = abs(val);
if( val > max )
{
max = val;
}
average += val;
}
average /= (double)numSamples;
done:
Pa_Terminate();
if(err)
{
fprintf(stderr, "An error occured while using the portaudio stream\n");
fprintf(stderr, "Error number: %d\n", err);
fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(err));
err = 1; /* Always return 0 or 1, but no other return codes. */
}
else
{
SF_INFO sfinfo;
sfinfo.channels = 1;
sfinfo.samplerate = data->sample_rate;
sfinfo.format = SF_FORMAT_FLAC | SF_FORMAT_PCM_16;
// open to file
printf("Writing to: %s\n", fileName);
SNDFILE * outfile = sf_open(fileName, SFM_WRITE, &sfinfo);
if (!outfile) return -1;
// prepare a 3 second long buffer (sine wave)
const int size = data->sample_rate * 3;
// write the entire buffer to the file
sf_write_raw(outfile, data->recordedSamples, size);
// force write to disk
sf_write_sync(outfile);
// don't forget to close the file
sf_close(outfile);
}
return err;
}
I'm not quite sure where I am going wrong, I know I need to be writing more frames. Any suggestions?
There seems to be something wrong with your assumptions about the sample format. In the callback you are using char * (single bytes) for the sample format, but in your libsndfile call you're opening a 16 bit file with SF_FORMAT_PCM_16.
This is not so good:
data->format_type = 1;
I recommend using one of the symbolic constants in the PortAudio library for sample formatting. Maybe you want a 16 bit one? And if so, you want to be using short* not char* in the PA callback.
Finally, if your channel count is not 1, the copy loops are incorrect:
for(i = 0; i < framesToCalc; i++){
*index_ptr++ = 0;
}
A "frame" contains data for all channels, so for example, if it's a stereo input your iteration needs to deal with both left and right channels like this:
for(i = 0; i < framesToCalc; i++){
*index_ptr++ = 0; // left
*index_ptr++ = 0; // right
}
Same for the other loops.

C / parse string, what is the easiest way

I have a string like that:
4;4=3;1=0,2=2,3=1,4=1,5=1;0003013340f59bce000002aaf01620e620198b2240002710;
It is separated into sections by ";" and each section can have one or more key/value pairs like 5=1 and so on, as you can see.
I want to parse it in pure C and I started working with strtok as I am showing in code here:
const wuint8 section_delimiter[] = ";";
const wuint8 field_delimiter[] = ",";
const wuint8 value_delimiter[] = "=";
printf("%s\n",data->msg);
token = strtok(data->msg,section_delimiter);
while(token != NULL) {
indicator = atoi(token);
printf("indicator: %d\n", indicator);
switch(indicator) {
case TYPE_1: {
printf("type: %d\n",TYPE_1);
wuint16 i, headerType, headerSubType;
for(i = 1; i < TP_MAX; i++) {
if(i == atoi(token)) {
token = strtok(NULL,value_delimiter);
headerType = i;
headerSubType = atoi(token);
break;
}
}
break;
}
case TYPE_2: {
printf("type: %d\n",TYPE_3);
break;
}
case TYPE_3: {
printf("type: %d\n",TYPE_3);
break;
}
case TYPE_4: {
printf("type: %d\n",TYPE_4);
break;
}
I am not sure how to do that correctly.
It also gets complicated, because not every string has the same structure, sometimes only one or two sections can be present. E.g.: 3;4=3;1=0,2=2,3=1,4=1,5=1;
Is there a how to do that showing the best and most convenient way?
strtok can't, AFAICR, be used in nested loops like this due to the global state it manages itself.
I suggest parsing each semicolon-delimited part out first, then handling them sequentially - or just implement something akin to strtok for your semicolon case yourself, then happily use strtok in the inner loop.
Using strcspn(). Fixed buffers, results go into global variables. data[] buffer is altered (and thus needs to be writable). YMMV
/*
It is separated into sections by ";" and each section can have one or more
key/value pairs like 5=1 and so on, as you can see. I want to parse it in
pure C and I started working with strtok as I am showing in code here:
*/
char data[] = "4;4=3;1=0,2=2,3=1,4=1,5=1;0003013340f59bce000002aaf01620e620198b2240002710;" ;
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct header {
int l;
int r;
} headers[123];
unsigned nheader;
int indicator;
char rest [123];
int tokenise(char * buff);
unsigned tokenise2(struct header *dst, char * buff);
/****************/
int tokenise(char * buff)
{
char *ptrs[14];
unsigned nptr;
unsigned len, pos;
ptrs[nptr=0] = NULL;
for (len = pos=0; buff[pos]; pos += len ) {
len = strcspn(buff+pos, ";");
ptrs[nptr++] = buff+pos;
ptrs[nptr] = NULL;
if (!buff[pos+len] ) break;
buff[pos+len] = 0;
len +=1;
}
if ( nptr> 0 && ptrs[0]) indicator = atoi(ptrs[0]); else indicator = -1;
if ( nptr> 1 && ptrs[1]) nheader = tokenise2 (headers, ptrs[1] ); else nheader = 0;
if ( nptr> 2 && ptrs[2]) nheader += tokenise2 (headers+nheader, ptrs[2] ); else nheader += 0;
if ( nptr> 3 && ptrs[3]) strcpy (rest, ptrs[3]); else rest[0] = 0;
return 0; /* or something useful ... */
}
unsigned tokenise2(struct header *target, char * buff)
{
char *ptrs[123];
unsigned nptr, iptr;
unsigned len, pos;
ptrs[nptr=0] = NULL;
for (len = pos=0; buff[pos]; pos += len ) {
len = strcspn(buff+pos, "," );
ptrs[nptr++] = buff+pos;
ptrs[nptr] = NULL;
if (!buff[pos+len] ) break;
buff[pos+len] = 0;
len +=1;
}
for ( iptr=0; iptr < nptr; iptr++) {
if (! ptrs[iptr] ) break;
len = strcspn(ptrs[iptr], "=" );
if (!len) break;
target[iptr].l = atoi (ptrs[iptr] );
target[iptr].r = atoi (ptrs[iptr]+len+1 );
}
return iptr; /* something useful ... */
}
int main(void)
{
int rc;
unsigned idx;
fprintf(stderr, "Org=[%s]\n", data );
rc = tokenise(data);
printf("Indicator=%d\n", indicator );
for (idx=0; idx < nheader; idx++) {
printf("%u: %d=%d\n", idx, headers[idx].l , headers[idx].r );
}
printf("Rest=%s\n", rest );
return 0;
}

Resources