FFI Encryption/Decryption with LuaJit - encryption
I'm trying to encrypt and decrypt using OpenSSL through FFI in LuaJIT - I've tried a lot of different variations but I'm not having a lot of luck. My code seems to return empty strings all the time.
I'm attempting to following the pattern described as part of the OpenSSL docs: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_decrypt.html
local ffi = require "ffi"
ffi.cdef[[
EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e);
void *malloc(size_t size);
void free(void *ptr);
int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *ctx);
int EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen, const unsigned char *in, size_t inlen);
int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *ctx);
int EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen, const unsigned char *in, size_t inlen);
]]
local s = "hello world"
local s_len = #s
local out_len1 = ffi.new("size_t[1]")
local ctx = ffi.C.EVP_PKEY_CTX_new(gen_key, nil)
if not ctx then
return nil
end
if ffi.C.EVP_PKEY_encrypt_init(ctx) == 0 then
return nil
end
if ffi.C.EVP_PKEY_encrypt(ctx, nil, out_len1, s, s_len) == 0 then
return nil
end
local buf = ffi.new("unsigned char[?]", out_len1[0])
if ffi.C.EVP_PKEY_encrypt(ctx, buf, out_len1, s, s_len) == 0 then
return nil
end
local s = ffi.string(buf, out_len1[0])
local s_len = #s
local out_len2 = ffi.new("size_t[1]")
if ffi.C.EVP_PKEY_decrypt_init(ctx) == 0 then
return nil
end
if ffi.C.EVP_PKEY_decrypt(ctx, nil, out_len2, s, s_len) == 0 then
return nil
end
local buf = ffi.new("unsigned char[?]", out_len2[0])
if ffi.C.EVP_PKEY_decrypt(ctx, buf, out_len2, s, s_len) == 0 then
return nil
end
return ffi.string(buf, out_len2[0])
I think your code has missing declarations for certain types such as EVP_PKEY_CTX, EVP_PKEY, etc. You would need to add the definition of those data types as well. Basically, ffi.cdef defines all function names that are going to be used through the FFI, so LuaJIT can parse them.
On the other hand, the call to those functions should be made through a library that actually implement those functions, such as OpenSSL, not ffi.C. The C namespace is used to access libc plus other C libraries such as libm and libdl. Example:
local ffi = require("ffi")
local ssl = ffi.load("ssl")
ffi.cdef[[
struct evp_pkey_ctx_st {
};
typedef struct evp_pkey_ctx_st EVP_PKEY_CTX;
struct evp_pkey_st {
};
typedef struct evp_pkey_st EVP_PKEY;
EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *key, void *b);
]]
local ctx = ssl.EVP_PKEY_CTX_new(gen_key, nil)
if ctx then
print("ctx created")
else
return nil
end
Answering my own question.
My original code did not strictly follow the C implementation example from OpenSSL since it only ever got the length of the encrypted data. It also never used any padding, it reused variables and wasn't split into methods and it was intentionally missing some dependancies to hide some implementation details.
The following code now works (standalone) and is better structured but it intentionally does no error checking and also will struggle with content longer than (KeyLength - 42).
To give some context this code is expecting Certificates and Keys in PEM format:
local ffi = require "ffi"
local ssl = ffi.load "ssl"
ffi.cdef[[
typedef struct bio_st BIO;
typedef struct bio_method_st BIO_METHOD;
BIO *BIO_new(BIO_METHOD *type);
BIO *BIO_new_mem_buf(void *buf, int len);
typedef struct evp_pkey_ctx_st EVP_PKEY_CTX;
typedef struct evp_pkey_st EVP_PKEY;
typedef struct engine_st ENGINE;
EVP_PKEY *EVP_PKEY_new(void);
void EVP_PKEY_free(EVP_PKEY *key);
typedef struct rsa_st RSA;
typedef int pem_password_cb(char *buf, int size, int rwflag, void *userdata);
RSA * PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **rsa, pem_password_cb *cb, void *u);
int EVP_PKEY_set1_RSA(EVP_PKEY *pkey,RSA *key);
EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e);
int EVP_PKEY_CTX_ctrl(EVP_PKEY_CTX *ctx, int keytype, int optype, int cmd, int p1, void *p2);
typedef struct x509_st X509;
X509 *PEM_read_bio_X509(BIO *bp, X509 **x, pem_password_cb *cb, void *u);
EVP_PKEY * X509_get_pubkey(X509 *x);
void X509_free(X509 *a);
int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *ctx);
int EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen, const unsigned char *in, size_t inlen);
int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *ctx);
int EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen, const unsigned char *in, size_t inlen);
]]
function encrypt(publicPEM, body)
local bioIn = ffi.new("unsigned char[?]", #publicPEM)
ffi.copy(bioIn, publicPEM, #publicPEM)
local bio = ffi.C.BIO_new_mem_buf(bioIn, -1)
local x509 = ffi.C.PEM_read_bio_X509(bio, nil, nil, nil)
ffi.gc(x509, ffi.C.X509_free)
local pKey = ffi.C.X509_get_pubkey(x509)
local ctx = ffi.C.EVP_PKEY_CTX_new(pKey, nil)
ffi.C.EVP_PKEY_encrypt_init(ctx)
-- Adds OEAP padding
ffi.C.EVP_PKEY_CTX_ctrl(ctx, 6, -1, 4097, 4, null)
-- Get the length
local outputLength = ffi.new("size_t[1]")
ffi.C.EVP_PKEY_encrypt(ctx, nil, outputLength, body, #body)
-- Encrypt into outputBuffer
local outputBuffer = ffi.new("unsigned char[?]", outputLength[0])
ffi.C.EVP_PKEY_encrypt(ctx, outputBuffer, outputLength, body, #body)
-- Turn it into a string
return ffi.string(outputBuffer, outputLength[0])
end
function decrypt(privatePEM, body)
local bioIn = ffi.new("unsigned char[?]", #privatePEM)
ffi.copy(bioIn, privatePEM, #privatePEM)
local bio = ffi.C.BIO_new_mem_buf(bioIn, -1)
if not bio then
return nil
end
local rsa = ffi.C.PEM_read_bio_RSAPrivateKey(bio, nil, nil, nil)
local pKey = ffi.C.EVP_PKEY_new()
ffi.C.EVP_PKEY_set1_RSA(pKey, rsa)
ctx = ffi.C.EVP_PKEY_CTX_new(pKey, nil)
ffi.C.EVP_PKEY_decrypt_init(ctx)
-- Adds OEAP padding
ffi.C.EVP_PKEY_CTX_ctrl(ctx, 6, -1, 4097, 4, null)
-- Get the length
local outputLength = ffi.new("size_t[1]")
ffi.C.EVP_PKEY_decrypt(ctx, nil, outputLength, body, #body)
-- Decrypt into outputBuffer
local outputBuffer = ffi.new("unsigned char[?]", outputLength[0])
ffi.C.EVP_PKEY_decrypt(ctx, outputBuffer, outputLength, body, #body)
-- Turn it into a string
return ffi.string(outputBuffer, outputLength[0])
end
io.write("Result: "..tostring(decrypt([[-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCoOeFfldK3bcsun1klFb+d3egSKkfq3oFAf6n6hQ2R3TrzY3Bb
+4hYSr5LhrP/HYOvc7bxk+T3GQe6C8B/7aOYJQ+DOweKoNK90uVEQRtFO8EZ4Z7r
JfaS2rhPuX71AnfwuvNG/TZ5UFruvwUqvs2hzw57gl+IzFgAtG8rtVX/zwIDAQAB
AoGAEeGFGRndue2LqTr6yLxVD7ykjDm+RzK7XlWzhZNa6+Qt/ezV5pEH3wqiy3hX
7Yf/lUiha3Ai6Dja32WcYnyp5Lf9vvxMcdyMlv3r78N7KUccXo6qvh0dE5VdrNxH
4U5oDs5U4OXaTC3/pCgBCV9w7IxbrLvsj1yKYQ7QBOLnJTECQQDQuHo6e1C2miyI
VZv5YzTdXucpshAhpDNf65Z214e/Ww1OOFNOw9sBaGGyOVv+F9EfHC8kO4pA32S7
HOx+knRlAkEAzlUpafetWL+Ht6yguyc9yBbfeoFL6v576GpMjkIV7w1oqmiDa5ep
P71U1evgYAwH6X0ZcnPXZwZU7eOkBZdeIwJBAKG2nPUcwC+KioBjHAMAa2As/Jug
m8EE8M0bwit32HRZfpihKWK4esG/dxpYOL9JArzA4IGJJBgZPXl/8ngqzsUCQQCy
Z1xJrcgKxoC4xeCsMf/vdCeDKyzTYXsNuGu9TVLdwcBQJ9IKQ7Yp0LD7ztnQ8lYd
AvfvyE3lXMoubvgxhXH1AkBDTjC3cHtlXygZcrqviIq/lOblm2voR2YW520079Yd
h0u2xcG80J53NkBk/q0IaTOamESi1IrD2ds3xp5mZulI
-----END RSA PRIVATE KEY-----
]], encrypt([[-----BEGIN CERTIFICATE-----
MIICYTCCAcoCCQCT+Ubn23B63TANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
UzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEN
MAsGA1UEChMES29uZzEWMBQGA1UECxMNSVQgRGVwYXJ0bWVudDESMBAGA1UEAxMJ
bG9jYWxob3N0MB4XDTE2MDExODEwNDUyOVoXDTE3MDExNzEwNDUyOVowdTELMAkG
A1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFu
Y2lzY28xDTALBgNVBAoTBEtvbmcxFjAUBgNVBAsTDUlUIERlcGFydG1lbnQxEjAQ
BgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqDnh
X5XSt23LLp9ZJRW/nd3oEipH6t6BQH+p+oUNkd0682NwW/uIWEq+S4az/x2Dr3O2
8ZPk9xkHugvAf+2jmCUPgzsHiqDSvdLlREEbRTvBGeGe6yX2ktq4T7l+9QJ38Lrz
Rv02eVBa7r8FKr7Noc8Oe4JfiMxYALRvK7VV/88CAwEAATANBgkqhkiG9w0BAQUF
AAOBgQAEC5ugqY6rOd3BbIam172OVQQwxcVx8BVfuiqX0zsFdBwTm/AvvdyJXRwo
64AqEIanvJF8Htq3Q9As6PNgHJ4eWEAZYTKlsf0PRM+d1T/uce3HY2SePuwr1Kqx
gFQjYTabjv361j8X3zB3HwrGsuED2UXxerXczszyiQNv/6BQlg==
-----END CERTIFICATE-----
]], "hello world"))).."\n")
Related
Casting a const void* pointer parameter into a const char* pointer, in memchr function
I have to reproduce the functioning of the memchr function, which returns the pointer of the first int c occurence. Of course, size_t n argument has been measured by func strlen before being sent to ft_memchr func parameter. But I keep on getting this compiling error (compiled with a main.c) " ft_memchr.c:10:21: error: operand of type 'const void' where arithmetic or pointer type is required if ((const char *)s[i] == (const char)c) " I'm clearly missing something... It seems I can't cast the const void* parameter into a const char* or even a char*, why is that ? Thank you. #include <string.h> void *ft_memchr(const void *s, int c, size_t n) { size_t i; i = 0; while (i < n) { if ((const char *)s[i] == (const char)c) return ((char *)s + i); } return (NULL); }
How to decrypt a file in virtual memory in C
I'm trying to decrypt a file in memory and this file was encrypted with openssl. For doing that, i use mmap for loading my encrypted file in memory like that: void* src = mmap(0, statbuf.st_size,PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); and duplicate it because i don't want to modify my original file void* dst = mmap(0, statbuf.st_size,PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); memcpy (dst, src, statbuf.st_size); At this step all is ok but i don't know what to do next. Initialy for testing purpose, i encrypt my file with the openssl command : system("openssl enc -aes-256-cbc -salt -in my_encryptedfile -out my_encryptedfile.enc -pass") and decrypt it with this command: system("openssl enc -d -aes-256-cbc -in my_encryptedfile.enc -out my_encryptedfile -pass pass:") But i can't use dst in this case, so i search and discovered EVP Symmetric Encryption and Decryption. link here Then i encrypted and decrypted my file with that code github code I try using Key and IV and the decryption in memory and it seem working but i have a problem that i don't understand. When i dump the buffer of my decripted file, i saw "SPACES/NULS" at the end of the file and i don't figure out why it display that. When i try to execute my binany in memory by calling this function : func() i got a segmentation fault Any clues? typedef void (*JittedFunc)(void); void* alloc_writable_memory(void *ptr, size_t size) { ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (ptr == (void*)-1) { perror("mmap"); return NULL; } return ptr; } int make_memory_executable(void* m, size_t size) { if (mprotect(m, size, PROT_READ |PROT_WRITE | PROT_EXEC) == -1) { perror("mprotect"); return -1; } return 0; } int do_crypt(char *in, char *out, int do_encrypt, int inlen) { /* Allow enough space in output buffer for additional block */ unsigned char outbuf[inlen + EVP_MAX_BLOCK_LENGTH]; int outlen; EVP_CIPHER_CTX *ctx; /* Bogus key and IV: we'd normally set these from * another source. */ unsigned char key[] = "0123456789abcdeF"; unsigned char iv[] = "1234567887654321"; //int n; printf("step1\n"); /* Don't set key or IV right away; we want to check lengths */ ctx = EVP_CIPHER_CTX_new(); printf("step2\n"); EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, NULL, NULL,do_encrypt); printf("step3\n"); OPENSSL_assert(EVP_CIPHER_CTX_key_length(ctx) == 16); OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == 16); /* Now we can set key and IV */ EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, do_encrypt); printf("step4\n"); if(!EVP_CipherUpdate(ctx, outbuf,&outlen, in, inlen)) { printf("test 2.1: %d %d\n", inlen, outlen); printf("step8\n"); /* Error */ EVP_CIPHER_CTX_free(ctx); return 0; } //BIO_dump_fp (stdout, (const char *)outbuf, outlen); printf(" test 2: %d %d\n", inlen, outlen); if(!EVP_CipherFinal_ex(ctx, outbuf, &outlen)) { printf("step11\n"); EVP_CIPHER_CTX_free(ctx); return 0; } //copy the decryted buffer in another memory space memcpy(out, outbuf, outlen); printf(" test 3: %d %d\n", inlen, outlen); //BIO_dump_fp (stdout, (const char *)outbuf, outlen); printf("step12\n"); //fwrite(outbuf, 1, outlen, out); printf("step13\n"); EVP_CIPHER_CTX_free(ctx); return 1; } int main() { FILE *src, *dst; char *src_mem, *dst_mem, *dst2_mem = NULL; struct stat statbuf; int fd; src = fopen("hello_encrypted", "rb"); if (!src) { /* Unable to open file for reading */ fprintf(stderr, "ERROR: fopen error: %s\n", strerror(errno)); return errno; } /*get the file des from a file*/ fd = fileno(src); /* find size of input file */ if (fstat (fd,&statbuf) < 0) {printf ("fstat error"); return 0; } /* go to the location corresponding to the last byte */ if (lseek (fd, statbuf.st_size - 1, SEEK_SET) == -1) {printf ("lseek error"); return 0; } if ((src_mem = mmap (0, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0)) == (caddr_t) -1) { printf ("mmap error for input"); return 0; } if ((dst_mem = mmap (0, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS , -1, 0)) == (caddr_t) -1) { printf ("mmap error for output"); return 0; } if ((dst2_mem = mmap (0, statbuf.st_size , PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS , -1, 0)) == (caddr_t) -1) { printf ("mmap error for output"); return 0; } memcpy(dst_mem, src_mem, statbuf.st_size); int n; /* 0 for decrypting or 1 for encrypting*/ n = do_crypt(dst_mem,dst2_mem, 0, statbuf.st_size); printf("%d\n", n); make_memory_executable(dst2_mem, statbuf.st_size); //dump of the decrypt binary BIO_dump_fp (stdout, (const char *)dst2_mem, statbuf.st_size); //try to launch the decrypted binary ==> segmentation fault JittedFunc func = dst2_mem; func(); fclose(src); return 0; }
I would remove from the first mmap the flag PROT_WRITE, because you dont want to modify the original file, I think you can also use PROT_PRIV, but have a look to what says the man of mmap. On the other hand for decrypt your buffer you can use many libraries (a lot of them based on openssl), I specially like CryptoPP but there are many others. Here is an example of how your code will look like on CryptoPP try { // CryptoPP::ECB_Mode< CryptoPP::AES >::Decryption d; // CrypoPP::CBC_Mode< CryptoPP::AES >::Decryption d; CrypoPP::CBC_CTS_Mode< CryptoPP::AES >::Decryption d; // What ever mode is best for you d.SetKey(key.data(), key.size()); // The StreamTransformationFilter removes // padding as required. CryptoPP::StringSource s((const uint8_t*)dst, length_dst, true, new CryptoPP::StreamTransformationFilter(d, new CryptoPP::StringSink(recovered) ) // StreamTransformationFilter ); // StringSource } catch(const CryptoPP::Exception& e) { std::cout << "ERROR decrypting:" << e.what() << std::endl; return; }
Libssh2 with WinCNG unable to obtain public key from private key with passphrase
I'm using libssh2 with WinCNG. It is failing to extract public key from a private key + passphrase pair. I see in wincng.c _libssh2_wincng_load_pem(LIBSSH2_SESSION *session, const char *filename, const char *passphrase, const char *headerbegin, const char *headerend, unsigned char **data, unsigned int *datalen) { FILE *fp; int ret; (void)passphrase; fp = fopen(filename, "r"); if (!fp) { return -1; } ret = _libssh2_pem_parse(session, headerbegin, headerend, fp, data, datalen); fclose(fp); return ret; } passphrase is no where used when private key file is read. Is it possible to use a private key + passphrase pair to extract publick key using libssh2 WinCNG? Thanks in advance
How to retrieve an int array that is stored in a table using PROGMEM?
I'm new to Arduino and currently learn to use PROGMEM to store variables so that I can save dynamic memory. I have 13 variables including these three below that I store using PROGMEM. Here are some of example of variables that I store and use it in my functions :- const unsigned int raw_0[62] PROGMEM = {2600,850,400,500,400,500,450,850,450,850,1350,850,450,450,400,500,400,450,450,400,450,450,450,450,400,450,900,850,900,850,900,450,450,850,900,850,900,850,450,450,900,450,400,450,400,900,450,450,450,400,450,450,450,450,400,450,450,450,450,400,450,}; const unsigned int raw_1[60] PROGMEM = {2600,850,450,450,450,450,450,850,450,850,1350,850,500,400,450,400,450,450,450,450,400,450,450,450,400,450,900,850,900,900,850,450,450,850,850,900,900,900,400,450,900,450,450,400,450,850,450,450,450,450,400,450,450,450,450,400,450,450,850,}; const unsigned int raw_a[100] PROGMEM = {3500,1700,400,450,450,1250,450,400,450,400,450,400,500,400,450,400,450,400,450,400,450,450,400,400,500,400,450,400,450,1300,400,450,450,400,450,400,450,400,450,400,450,400,500,350,500,400,450,400,450,1300,400,400,500,400,450,400,450,400,450,450,400,450,450,400,450,400,450,400,450,400,450,450,400,450,450,400,450,1250,450,400,450,400,500,400,450,400,450,400,450,400,450,400,450,1300,450,400,450,1250,450,}; Here is the table that store the variables. I learn this approach from Arduino website; https://www.arduino.cc/en/Reference/PROGMEM . const unsigned int* const myTable[13] PROGMEM = { raw_0, raw_1, raw_2, raw_3, raw_4, raw_5, raw_6, raw_7, raw_8, raw_9, raw_a, raw_b, raw_c}; My problem is, how do I retrieve these variables using PROGMEM such as raw_1 and raw_a ? This is what I did but it did not work :- unsigned int * ptr = (unsigned int *) pgm_read_word (&myTable [1]); irsend.sendRaw(ptr,62,38); Most of examples that I found, they use String or char datatype but in my case, I use array integer.
The ptr is also pointer to PROGMEM, so you have to read the value (or values in this case) by pgm_read_word. The IR library doesn't support that at all (I hope it's the correct one). Anyway sendRaw implementation is this: void IRsend::sendRaw (const unsigned int buf[], unsigned int len, unsigned int hz) { // Set IR carrier frequency enableIROut(hz); for (unsigned int i = 0; i < len; i++) { if (i & 1) space(buf[i]) ; else mark (buf[i]) ; } space(0); // Always end with the LED off } And all used methods are public, so you can implement your own function to do the same: void mySendRaw (IRsend & dev, const unsigned int buf[], unsigned int len, unsigned int khz) { // Set IR carrier frequency dev.devenableIROut(khz); for (unsigned int i = 0; i < len; i++) { if (i & 1) dev.space(pgm_read_word(buf+i)); else dev.mark (pgm_read_word(buf+i)); } dev.space(0); // Always end with the LED off } // And usage: mySendRaw(irsend, (const uint16_t*)pgm_read_word(myTable+1), 62, 38); However the size of arrays should be stored somewhere too, so you can use something like: byte cmd = 1; mySendRaw(irsend, (const uint16_t*)pgm_read_word(myTable+cmd), pgm_read_word(myTableLenghts+cmd), 38);
GMainContext have ref_count > 0 after unref
I am not getting ref_count to decrease properly for my GMainContext. The example program here is a small version of a large program (which uses threads, hence the need to create a context and push it on the thread). GMainLoop *loop; GMainContext *ctx; struct conn { GSocketClient *client; GSocketConnection *conn; GInputStream *in; GOutputStream *out; gchar data[8192]; unsigned int count; }; static void read_done_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) { struct conn *c = (struct conn *)user_data; gssize len = g_input_stream_read_finish(c->in, res, NULL); g_input_stream_read_async(c->in, c->data, sizeof c->data / sizeof *c->data, G_PRIORITY_DEFAULT, NULL, read_done_cb, c); if (c->count++ == 1) { printf("End of life as I know it...\n"); g_main_loop_quit(loop); } } static void write_done_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) { } static void connect_done_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) { printf("## %s\n", __FUNCTION__); struct conn *c = (struct conn *)user_data; c->conn = g_socket_client_connect_to_host_finish(c->client, res, NULL); c->in = g_io_stream_get_input_stream(G_IO_STREAM(c->conn)); c->out = g_io_stream_get_output_stream(G_IO_STREAM(c->conn)); char *data = "GET /axis-cgi/mjpg/video.cgi HTTP/1.0\r\n\r\n"; g_output_stream_write_async(c->out, data, strlen(data), G_PRIORITY_DEFAULT, NULL, write_done_cb, c); g_input_stream_read_async(c->in, c->data, sizeof c->data / sizeof *c->data, G_PRIORITY_DEFAULT, NULL, read_done_cb, c); } int main(int argc, char **argv) { g_type_init(); struct conn *c = g_malloc0(sizeof *c); ctx = g_main_context_new(); loop = g_main_loop_new(ctx, FALSE); g_main_context_push_thread_default(ctx); c->client = g_socket_client_new(); g_socket_client_connect_to_host_async(c->client, "10.85.25.20", 80, NULL, connect_done_cb, c); g_main_loop_run(loop); g_io_stream_close(G_IO_STREAM(c->conn), NULL, NULL); g_object_unref(c->client); g_object_unref(c->conn); g_main_context_pop_thread_default(ctx); g_main_loop_unref(loop); g_main_context_unref(ctx); return 0; } Using gdb, inserting breakpoint just before return I can see that ctx still have one ref count: (gdb) p ctx->ref_count $2 = 1 If I do another g_main_context_unref(ctx); everything shuts down as expected. I do not understand where I get this ownership though. Thanks in advance for your help
I found the error. I read_done_cb I issued another g_input_stream_read_async and immediately after quitting the main loop. g_input_stream_read_async upped the ref_count but GMainLoop never got a chance to return to my callback (and decreasing the ref_count on my GMainContext). Moving the call to g_input_stream_read_async in my callback to below the if statement static void read_done_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) { struct conn *c = (struct conn *)user_data; gssize len = g_input_stream_read_finish(c->in, res, NULL); if (c->count++ == 1) { printf("End of life as I know it...\n"); g_main_loop_quit(loop); } g_input_stream_read_async(c->in, c->data, sizeof c->data / sizeof *c->data, G_PRIORITY_DEFAULT, NULL, read_done_cb, c); } correctly resolved the number of ref counts on my main context. Silly mistake. Hopefully someone will find some use of my post at least.
g_main_context_new(), g_main_loop_new(), and g_main_context_push_thread_default() all ref the context. g_main_context_pop_thread_default(), g_main_loop_unref(), and g_main_context_unref() all unref it. So your intuition is sound. I would use a watchpoint in gdb: watch ctx->ref_count to find out where the extra reference is being added.