ASIO UDP client never receiving messages - c++

I'm trying to write a DNS Resolver with user-supplied resolvers(just a text file with several IP addresses that could be used for querying) using the standalone ASIO/C++ library and I have failed on every attempt to make the receiver work. All the resolvers do not seem to be responding(udp::receive_from) to the query I'm sending. However, when I try to use the same resolver file with an external library like dnslib, they work like charm, so problem lies in my code. Here's the code I'm using to send data to the DNS servers.
struct DNSPktHeader
{
uint16_t id{};
uint16_t bitfields{};
uint16_t qdcount{};
uint16_t ancount{};
uint16_t nscount{};
uint16_t arcount{};
};
// dnsname, for example is -> google.com
// dns_resolver is a list of udp::endpoint of IPv4 address on port 53.
// ip is the final result
// returns 0 on success and negative value on failure
int get_host_by_name( char const *dnsname, std::vector<udp::endpoint> const & dns_resolvers, OUT uint16_t* ip )
{
uint8_t netbuf[128]{};
char const *funcname = "get_host_by_name";
uint16_t const dns_id = rand() % 2345; // warning!!! Simply for testing purpose
DNSPktHeader dns_qry{};
dns_qry.id = dns_id;
dns_qry.qdcount = 1;
dns_qry.bitfields = 0x8; // set the RD field of the header to 1
// custom_htons sets the buffer pointed to by the second argument netbuf
// to the htons of the first argument
custom_htons( dns_qry.id, netbuf + 0 );
custom_htons( dns_qry.bitfields, netbuf + 2 );
custom_htons( dns_qry.qdcount, netbuf + 4 );
custom_htons( dns_qry.ancount, netbuf + 6 );
custom_htons( dns_qry.nscount, netbuf + 8 );
custom_htons( dns_qry.arcount, netbuf + 10 );
unsigned char* question_start = netbuf + sizeof( DNSPktHeader ) + 1;
// creates the DNS question segment into netbuf's specified starting index
int len = create_question_section( dnsname, (char**) &question_start, thisdns::dns_record_type::DNS_REC_A,
thisdns::dns_class::DNS_CLS_IN );
if( len < 0 ){
fmt::print( stderr, "{}: {} ({})\n", funcname, dnslib_errno_strings[DNSLIB_ERRNO_BADNAME - 1], dnsname );
return -EFAULT;
}
len += sizeof( DNSPktHeader );
fmt::print( stdout, "{}: Submitting DNS A-record query for domain name ({})\n", funcname, dnsname );
asio::error_code resolver_ec{};
udp::socket udp_socket{ DNSResolver::GetIOService() };
udp_socket.open( udp::v4() );
// set 5 seconds timeout on receive and reuse the address
udp_socket.set_option( asio::ip::udp::socket::reuse_address( true ) );
udp_socket.set_option( asio::detail::socket_option::integer<SOL_SOCKET, SO_RCVTIMEO>{ 5'000 } );
udp_socket.bind( udp::endpoint{ asio::ip::make_address( "127.0.0.1" ), 53 } );
std::size_t bytes_read = 0, retries = 1;
int const max_retries = 10;
asio::error_code receiver_err{};
uint8_t receive_buf[0x200]{};
udp::endpoint default_receiver{};
do{
udp::endpoint const & resolver_endpoint{ dns_resolvers[retries] };
int bytes_sent = udp_socket.send_to( asio::buffer( netbuf, len ), resolver_endpoint, 0, resolver_ec );
if( bytes_sent < len || resolver_ec ){
fmt::print( stderr, "{}: (found {}, expected {})\n", funcname, i, sizeof( DNSPktHeader ) );
return -EFAULT;
}
// ======== the problem ==============
bytes_read = udp_socket.receive_from( asio::buffer( receive_buf, sizeof( receive_buf ) ), default_receiver, 0,
receiver_err );
// bytes_read always return 0
if( receiver_err ){
fmt::print( stderr, "{}\n\n", receiver_err.message() );
}
} while( bytes_read == 0 && retries++ < max_retries );
//...
}
I have tried my best but it clearly isn't enough. Could you please take a look at this and help figure where the problem lies? It's my very first time using ASIO on any real-life project.
Don't know if this would be relevant but here's create_question_section.
int create_question_section( const char *dnsname, char** buf, thisdns::dns_record_type type, thisdns::dns_class class_ )
{
char const *funcname = "create_question_section";
if( dnsname[0] == '\0' ){ // Blank DNS name?
fmt::print( stderr, "{}: Blank DNS name?\n", funcname );
return -EBADF;
}
uint8_t len{};
int index{};
int j{};
bool found = false;
do{
if( dnsname[index] != '.' ){
j = 1;
found = false;
do{
if( dnsname[index + j] == '.' || dnsname[index + j] == '\0' ){
len = j;
strncpy( *buf, (char*) &len, 1 );
++( *buf );
strncpy( *buf, (char*) dnsname + index, j );
( *buf ) += j;
found = true;
if( dnsname[index + j] != '\0' )
index += j + 1;
else
index += j;
} else{
j++;
}
} while( !found && j < 64 );
} else{
fmt::print( stderr, "{}: DNS addresses can't start with a dot!\n", funcname );
return -EBADF; // DNS addresses can't start with a dot!
}
} while( dnsname[index] );
uint8_t metadata_buf[5]{};
custom_htons( (uint16_t)type, metadata_buf + 1 );
custom_htons( (uint16_t)class_, metadata_buf + 3 );
strncpy( *buf, (char*) metadata_buf, sizeof(metadata_buf) );
return sizeof( metadata_buf ) + index + 1;
}

There are at least two issues why it's not working for you. They all boil down to the fact that the DNS packet you send out is malformed.
This line
unsigned char* question_start = netbuf + sizeof( DNSPktHeader ) + 1;
sets the pointer into the buffer one position farther than you want. Instead of starting the encoded FQDN at position 12 (as indexed from 0) it starts at position 13. What that means is that the DNS server sees a zero length domain name and some garbage record type and class and ignores the rest. And so it decides not to respond to your query at all. Just get rid of +1.
Another possible issue could be in encoding all the records with custom_htons(). I have no clue how it's implemented and so cannot tell you whether it works correctly.
Furthermore, although not directly responsible for your observed behaviour, the following call to bind() will have zero effect unless you run the binary as root (or with appropriate capabilities on linux) because you are trying to bind to a privileged port
udp_socket.bind( udp::endpoint{ asio::ip::make_address( "127.0.0.1" ), 53 } );
Also, this dns_qry.bitfields = 0x8; doesn't do what you want. It should be dns_qry.bitfields = 0x80;.
Check this and this RFC out for reference on how to form a valid DNS request.
Important note: I would strongly recommend to you not to mix C++ with C. Pick one but since you tagged C++ and use Boost and libfmt, stick with C++. Replace all your C-style casts with appropriate C++ versions (static_cast, reinterpret_cast, etc.). Instead of using C-style arrays, use std::array, don't use strncpy, etc.

Related

get packet number in libpcap callback

I'm using libpcap to process the WS output.
My question is: can I have access in the packet number in the pcap_loop callback? Or I will have to use a static variable?
EDIT:
As requested:
long Foo:Main()
{
handle = pcap_open_dead( DLT_EN10MB, MAX_PACKET_SIZE );
if( !handle )
{
}
dumper = pcap_dump_open( handle, fileOut.ToString() );
if( !dumper )
{
}
handle = pcap_open_offline( fileNameStr.ToString(), errbuf );
if( !handle )
{
}
if( pcap_compile( handle, &fp, FltString.ToString(), 0, net ) == PCAP_ERROR )
{
}
// Set filter for JREAP only
if( pcap_setfilter( handle, &fp ) == PCAP_ERROR )
{
}
unchar *uncharThis = reinterpret_cast<unchar*>( this );
// The pcap_loop is implemented like:
// for( int i = 0; i < num_of_packets; i++ )
// ProcessPackets();
// where i is the current packet number to process
int ret_val = pcap_loop( handle, 0, ProcessPackets, uncharThis );
if( ret_val == PCAP_ERROR )
{
}
}
bool Foo::ProcessPackets(unchar *userData, const struct pcap_pkthdr *pkthdr, const unchar *packet)
{
// This function will be called for every packet in the pcap file
// that satisfy the filter condition.
// Inside this function do I have access to the packet number.
// Do I have an access to the variable `i` from the comment above
// Or I will have to introduce a static variable here?
}
libpcap does not keep track of the ordinal numbers of packets, so you'll have to maintain a packet count in your code.

Two way communication using sockets

I am trying to implement two way communication using sockets and not quite sure where I'm going wrong. I have an application that launches a child application, the child application then tries to communicate with the application that launched it, but I am not getting anything.
In the application that launches the child:
int clsSocketThread::initialiseSocket(bool blnIsModule, QString strPurpose) {
const char* cpszLocalHost = "localhost";
//Get the socket
int intSocket = socket(AF_INET, SOCK_STREAM, 0);
if ( intSocket == 0 ) {
clsDebugService::exitWhenDebugQueueEmpty("Failed to create socket!");
}
struct hostent* pHostEntry = gethostbyname(cpszLocalHost);
if ( pHostEntry == nullptr ) {
clsDebugService::exitWhenDebugQueueEmpty("Unable to resolve ip address!");
}
//Initliase and get address of localhost
struct sockaddr_in srvAddr;
bzero((char*)&srvAddr, sizeof(srvAddr));
//Set-up server address
memcpy(&srvAddr.sin_addr, pHostEntry->h_addr_list[0], pHostEntry->h_length);
srvAddr.sin_family = AF_INET;
srvAddr.sin_port = htons(clsSocketThread::mscuint16Port);
char* pszIP = inet_ntoa(srvAddr.sin_addr);
if ( pszIP != nullptr ) {
qdbg() << "Setting up socket on ip: " << pszIP
<< ", port: " << clsSocketThread::mscuint16Port
<< ((strPurpose.isEmpty() == true) ? "" : strPurpose);
}
socklen_t tSvrAddr = sizeof(srvAddr);
int intRC;
#if !defined(STANDALONE)
if ( blnIsModule == true ) {
intRC = inet_pton(srvAddr.sin_family, pszIP, &srvAddr.sin_addr);
if ( intRC <= 0 ) {
clsDebugService::exitWhenDebugQueueEmpty("Invalid address not supported!");
}
intRC = ::connect(intSocket, (const struct sockaddr*)&srvAddr, tSvrAddr);
} else
#endif
{
intRC = bind(intSocket, (const struct sockaddr*)&srvAddr, tSvrAddr);
}
if ( intRC < 0 ) {
clsDebugService::exitWhenDebugQueueEmpty("Socket operation failed!");
}
if ( blnIsModule != true && listen(intSocket, 5) < 0 ) {
clsDebugService::exitWhenDebugQueueEmpty("Cannot listen to socket!");
}
return intSocket;
}
This function is used by both the launcher and the child, when the child calls it the first parameter is true. I've run both in debuggers and all the function calls are successful and there are no errors.
In the launching application I have a thread:
void clsSocketThread::serverSocketBody() {
if ( mintSocket == 0 ) {
mintSocket = clsSocketThread::initialiseSocket();
}
QByteArray qarybytBuffer;
char arycBuffer[2048];
int intNewSocket = 0;
size_t tBufferSize = sizeof(arycBuffer);
QJsonObject objJSON;
while( mpThread != nullptr ) {
if ( intNewSocket <= 0 ) {
struct sockaddr_in cliAddr;
socklen_t tCliLen = sizeof(cliAddr);
intNewSocket = accept(mintSocket, (struct sockaddr*)&cliAddr, &tCliLen);
if ( intNewSocket < 0 ) {
continue;
}
}
//Read from the other socket!
bzero(arycBuffer, tBufferSize);
ssize_t tRead = read(intNewSocket, arycBuffer, tBufferSize);
if ( tRead <= 0 ) {
continue;
}
qarybytBuffer = QByteArray(arycBuffer, (int)tRead);
int intIdx = qarybytBuffer.indexOf(clsJSON::msccOpenCurlyBracket)
,intIdx2 = qarybytBuffer.lastIndexOf(clsJSON::msccCloseCurlyBracket);
if ( intIdx >= 0 && intIdx2 > intIdx ) {
qarybytBuffer = qarybytBuffer.mid(intIdx, intIdx2 - intIdx + 1).trimmed();
QJsonObject objJSON(clsJSON(&qarybytBuffer).toQJsonObject());
if ( objJSON.contains(clsJSON::mscszMsgType) == true ) {
qdbg() << "[RX]Data: " << arycBuffer;//HACK
clsJSON::blnDecodeAccordingToType(&objJSON);
}
}
}
}
I'm not receiving any messages from the child. Both applications are set-up to communicate on localhost:8123
This is operating system specific.
For Linux, read Advanced Linux Programming then syscalls(2), socket(7), unix(7), fifo(7), pipe(7)
With Qt, consider using QSocketNotifier in your main thread.
You might also want to use POCO, ONCRPC, JSONRPC, Wt (perhaps with libcurl) or libonion.
You could get some inspiration by studying the C++ source code of Qt, of POCO, of VMIME.
Be aware that in many (but not all) cases, a single send(2) -or write(2)- on emitter side may correspond to several recv(2) -or read(2)- on the receiving side (and vice versa), at least with TCP on different machines. So you need some event loop (often around poll(2)...) and documented conventions on application-level message formats. Then SMTP or HTTP could be inspirational (and in some cases, useful).

How can I make this code play the wave file for longer?

I couldn't figure out how to create my own sound player, so I have opted to use one from ChiliTomatoNoodle's framework.
The issue I'm having, however, is I have a 180s wave file, that's only playing the first second or so. What do I have to do to make it play longer?
Sound.h:
#pragma once
#include <windows.h>
#include <mmsystem.h>
#include <dsound.h>
#include <stdio.h>
class DSound;
class Sound
{
friend DSound;
public:
Sound( const Sound& base );
Sound();
~Sound();
const Sound& operator=( const Sound& rhs );
void Play( int attenuation = DSBVOLUME_MAX );
private:
Sound( IDirectSoundBuffer8* pSecondaryBuffer );
private:
IDirectSoundBuffer8* pBuffer;
};
class DSound
{
private:
struct WaveHeaderType
{
char chunkId[4];
unsigned long chunkSize;
char format[4];
char subChunkId[4];
unsigned long subChunkSize;
unsigned short audioFormat;
unsigned short numChannels;
unsigned long sampleRate;
unsigned long bytesPerSecond;
unsigned short blockAlign;
unsigned short bitsPerSample;
char dataChunkId[4];
unsigned long dataSize;
};
public:
DSound( HWND hWnd );
~DSound();
Sound CreateSound( char* wavFileName );
private:
DSound();
private:
IDirectSound8* pDirectSound;
IDirectSoundBuffer* pPrimaryBuffer;
};
Sound.cpp:
#include "Sound.h"
#include <assert.h>
#pragma comment(lib, "dsound.lib")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "winmm.lib" )
DSound::DSound( HWND hWnd )
: pDirectSound( NULL ),
pPrimaryBuffer( NULL )
{
HRESULT result;
DSBUFFERDESC bufferDesc;
WAVEFORMATEX waveFormat;
result = DirectSoundCreate8( NULL,&pDirectSound,NULL );
assert( !FAILED( result ) );
// Set the cooperative level to priority so the format of the primary sound buffer can be modified.
result = pDirectSound->SetCooperativeLevel( hWnd,DSSCL_PRIORITY );
assert( !FAILED( result ) );
// Setup the primary buffer description.
bufferDesc.dwSize = sizeof(DSBUFFERDESC);
bufferDesc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
bufferDesc.dwBufferBytes = 0;
bufferDesc.dwReserved = 0;
bufferDesc.lpwfxFormat = NULL;
bufferDesc.guid3DAlgorithm = GUID_NULL;
// Get control of the primary sound buffer on the default sound device.
result = pDirectSound->CreateSoundBuffer( &bufferDesc,&pPrimaryBuffer,NULL );
assert( !FAILED( result ) );
// Setup the format of the primary sound bufffer.
// In this case it is a .WAV file recorded at 44,100 samples per second in 16-bit stereo (cd audio format).
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.nSamplesPerSec = 44100;
waveFormat.wBitsPerSample = 16;
waveFormat.nChannels = 2;
waveFormat.nBlockAlign = (waveFormat.wBitsPerSample / 8) * waveFormat.nChannels;
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
waveFormat.cbSize = 0;
// Set the primary buffer to be the wave format specified.
result = pPrimaryBuffer->SetFormat( &waveFormat );
assert( !FAILED( result ) );
}
DSound::~DSound()
{
if( pPrimaryBuffer )
{
pPrimaryBuffer->Release();
pPrimaryBuffer = NULL;
}
if( pDirectSound )
{
pDirectSound->Release();
pDirectSound = NULL;
}
}
// must be 44.1k 16bit Stereo PCM Wave
Sound DSound::CreateSound( char* wavFileName )
{
int error;
FILE* filePtr;
unsigned int count;
WaveHeaderType waveFileHeader;
WAVEFORMATEX waveFormat;
DSBUFFERDESC bufferDesc;
HRESULT result;
IDirectSoundBuffer* tempBuffer;
IDirectSoundBuffer8* pSecondaryBuffer;
unsigned char* waveData;
unsigned char* bufferPtr;
unsigned long bufferSize;
// Open the wave file in binary.
error = fopen_s( &filePtr,wavFileName,"rb" );
assert( error == 0 );
// Read in the wave file header.
count = fread( &waveFileHeader,sizeof( waveFileHeader ),1,filePtr );
assert( count == 1 );
// Check that the chunk ID is the RIFF format.
assert( (waveFileHeader.chunkId[0] == 'R') &&
(waveFileHeader.chunkId[1] == 'I') &&
(waveFileHeader.chunkId[2] == 'F') &&
(waveFileHeader.chunkId[3] == 'F') );
// Check that the file format is the WAVE format.
assert( (waveFileHeader.format[0] == 'W') &&
(waveFileHeader.format[1] == 'A') &&
(waveFileHeader.format[2] == 'V') &&
(waveFileHeader.format[3] == 'E') );
// Check that the sub chunk ID is the fmt format.
assert( (waveFileHeader.subChunkId[0] == 'f') &&
(waveFileHeader.subChunkId[1] == 'm') &&
(waveFileHeader.subChunkId[2] == 't') &&
(waveFileHeader.subChunkId[3] == ' ') );
// Check that the audio format is WAVE_FORMAT_PCM.
assert( waveFileHeader.audioFormat == WAVE_FORMAT_PCM );
// Check that the wave file was recorded in stereo format.
assert( waveFileHeader.numChannels == 2 );
// Check that the wave file was recorded at a sample rate of 44.1 KHz.
assert( waveFileHeader.sampleRate == 44100 );
// Ensure that the wave file was recorded in 16 bit format.
assert( waveFileHeader.bitsPerSample == 16 );
// Check for the data chunk header.
assert( (waveFileHeader.dataChunkId[0] == 'd') &&
(waveFileHeader.dataChunkId[1] == 'a') &&
(waveFileHeader.dataChunkId[2] == 't') &&
(waveFileHeader.dataChunkId[3] == 'a') );
// Set the wave format of secondary buffer that this wave file will be loaded onto.
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.nSamplesPerSec = 44100;
waveFormat.wBitsPerSample = 16;
waveFormat.nChannels = 2;
waveFormat.nBlockAlign = (waveFormat.wBitsPerSample / 8) * waveFormat.nChannels;
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
waveFormat.cbSize = 0;
// Set the buffer description of the secondary sound buffer that the wave file will be loaded onto.
bufferDesc.dwSize = sizeof(DSBUFFERDESC);
bufferDesc.dwFlags = DSBCAPS_CTRLVOLUME;
bufferDesc.dwBufferBytes = waveFileHeader.dataSize;
bufferDesc.dwReserved = 0;
bufferDesc.lpwfxFormat = &waveFormat;
bufferDesc.guid3DAlgorithm = GUID_NULL;
// Create a temporary sound buffer with the specific buffer settings.
result = pDirectSound->CreateSoundBuffer( &bufferDesc,&tempBuffer,NULL );
assert( !FAILED( result ) );
// Test the buffer format against the direct sound 8 interface and create the secondary buffer.
result = tempBuffer->QueryInterface( IID_IDirectSoundBuffer8,(void**)&pSecondaryBuffer );
assert( !FAILED( result ) );
// Release the temporary buffer.
tempBuffer->Release();
tempBuffer = 0;
// Move to the beginning of the wave data which starts at the end of the data chunk header.
fseek( filePtr,sizeof(WaveHeaderType),SEEK_SET );
// Create a temporary buffer to hold the wave file data.
waveData = new unsigned char[ waveFileHeader.dataSize ];
assert( waveData );
// Read in the wave file data into the newly created buffer.
count = fread( waveData,1,waveFileHeader.dataSize,filePtr );
assert( count == waveFileHeader.dataSize);
// Close the file once done reading.
error = fclose( filePtr );
assert( error == 0 );
// Lock the secondary buffer to write wave data into it.
result = pSecondaryBuffer->Lock( 0,waveFileHeader.dataSize,(void**)&bufferPtr,(DWORD*)&bufferSize,NULL,0,0 );
assert( !FAILED( result ) );
// Copy the wave data into the buffer.
memcpy( bufferPtr,waveData,waveFileHeader.dataSize );
// Unlock the secondary buffer after the data has been written to it.
result = pSecondaryBuffer->Unlock( (void*)bufferPtr,bufferSize,NULL,0 );
assert( !FAILED( result ) );
// Release the wave data since it was copied into the secondary buffer.
delete [] waveData;
waveData = NULL;
return Sound( pSecondaryBuffer );
}
Sound::Sound( IDirectSoundBuffer8* pSecondaryBuffer )
: pBuffer( pSecondaryBuffer )
{}
Sound::Sound()
: pBuffer( NULL )
{}
Sound::Sound( const Sound& base )
: pBuffer( base.pBuffer )
{
pBuffer->AddRef();
}
Sound::~Sound()
{
if( pBuffer )
{
pBuffer->Release();
pBuffer = NULL;
}
}
const Sound& Sound::operator=( const Sound& rhs )
{
this->~Sound();
pBuffer = rhs.pBuffer;
pBuffer->AddRef();
return rhs;
}
// attn is the attenuation value in units of 0.01 dB (larger
// negative numbers give a quieter sound, 0 for full volume)
void Sound::Play( int attn )
{
attn = max( attn,DSBVOLUME_MIN );
HRESULT result;
// check that we have a valid buffer
assert( pBuffer != NULL );
// Set position at the beginning of the sound buffer.
result = pBuffer->SetCurrentPosition( 0 );
assert( !FAILED( result ) );
// Set volume of the buffer to attn
result = pBuffer->SetVolume( attn );
assert( !FAILED( result ) );
// Play the contents of the secondary sound buffer.
result = pBuffer->Play( 0,0,0 );
assert( !FAILED( result ) );
}
Thanks for your help in advance!
Assuming you have a .wav file, and you are loading the sound file somewhere along the lines of:
yourSound = audio.CreateSound("fileName.WAV"); //Capslock on WAV
yourSound.Play();
With this comes the declaration of the Sound in the header:
Sound yourSound;
Now because you have probably done this already and this is not working it likely has to do with your file as playing sounds 160 seconds+ should not be a problem.
Are you using a .WAV file for the sound? If so did you happen to convert that (as it is probably a background sound?). If you did try converting it with this converter:
Converter MP3 -> WAV
Please let me know if this works!
Your buffer is probably only large enough to play the first second or so. What you need to do is setup "notifications". See the documentation.
Notifications are a way to ask the audio hardware to let you know when they have reached a specific point in the buffer.
The idea is to setup a notification in the middle of the buffer and at the end of the buffer. When you receive the notification from the notification in the middle, you fill the first half of the buffer with more data. When you receive the notification from the end, you fill the second half of the buffer with more data. This way, you can stream an infinite amount of data with a single buffer.

kernel communication

I want to send a array of data to kernel space , ( i have used call back function in my kext)
problem is when i use send function i see something weird that i explain in 2 scenario:
1)
...
char f[]={'1','2','3','4','5','6'};
send (sock,f,sizeof(f),0);
well, when i printf what i receive in kext:
123456
2)
...
// i replace f[2] with 0
char f[]={'1','2',0,'4','5','6'};
send (sock,f,sizeof(f),0);
but this time, when i printf what i receive in kext:
120000
it seems that send function make zero every byte after first 0 byte?
what is going on? is this a send function bug?
i used xcode 4.1 and i my os is lion
here is user space part:
int main(int argc, char* const*argv)
{
struct ctl_info ctl_info;
struct sockaddr_ctl sc;
char str[MAX_STRING_LEN];
int sock = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
if (sock < 0)
return -1;
bzero(&ctl_info, sizeof(struct ctl_info));
strcpy(ctl_info.ctl_name, "pana.ifmonitor.nke.foo");
if (ioctl(sock, CTLIOCGINFO, &ctl_info) == -1)
return -1;
bzero(&sc, sizeof(struct sockaddr_ctl));
sc.sc_len = sizeof(struct sockaddr_ctl);
sc.sc_family = AF_SYSTEM;
sc.ss_sysaddr = SYSPROTO_CONTROL;
sc.sc_id = ctl_info.ctl_id;
sc.sc_unit = 0;
if (connect(sock, (struct sockaddr *)&sc, sizeof(struct sockaddr_ctl)))
return -1;
unsigned char data_send[]={'a','l','i','0','1','2','4','l','i',0,'1','2','4','l','i','0','1'};
size_t data_recive;
int j=0;
char data_rcv[8192];
send( sock, data_send, 17*sizeof(char), 10 );
printf("\n");
sleep(1);
close(sock);
return 0;
}
and this is some part of kernel space code that is responsible for getting user space data:
errno_t EPHandleWrite(kern_ctl_ref ctlref, unsigned int unit, void *userdata,mbuf_t m, int flags)
{
printf("\n EPHandleWrite called---------------------- \n");
//char data_rec[50];
//unsigned char *ptr = (unsigned char*)mbuf_data(m);
//char ch;
//mbuf_copydata(m, 0, 50, data_rec);
//strncpy(&ch, ptr, 1 );
size_t data_lenght;
data_lenght = mbuf_pkthdr_len(m);
char data_receive[data_lenght];
strncpy( data_receive, ( char * ) mbuf_data(m) , data_lenght );
printf("data recied %lu\n",data_lenght);
for(int i=0;i<data_lenght;++i)
{
printf("%X ",data_receive[i]);
}
return 0
}
well, it print in console:
61 6C 69 30 31 32 34 6C 69 0 0 0 0 0 0 0 0
and when i change send data to:
{'a','l','i','0','1','2','4','l','i',**'0'**,'1','2','4','l','i','0','1'};
i get correct, in fact i get all 0 after first zero byte in send data
The problem is the strncpy line - if you look at the documentation for strncpy, you'll notice that it only copies until it reaches a 0 byte, so it's only suitable for dealing with C strings. If you need to copy arbitrary binary data, use memcpy.

How to use AudioQueue to play a sound for Mac OSX in C++

I am trying to play a sound on OSX, from a buffer (eg: Equivalent of Windows "PlaySound" function).
I have put together some C++ code to play audio with AudioQueue (as it is my understanding that this is the easiest way to play audio on OSX).
However, no sound is ever generated, and the audio callback function is never called.
Does anybody know what I'm doing wrong, or does anyone have a simple C/C++ example of how to play a sound on OSX?
#include
#include
#define BUFFER_COUNT 3
static struct AQPlayerState {
AudioStreamBasicDescription desc;
AudioQueueRef queue;
AudioQueueBufferRef buffers[BUFFER_COUNT];
unsigned buffer_size;
} state;
static void audio_callback (void *aux, AudioQueueRef aq, AudioQueueBufferRef bufout)
{
printf("I never get called!\n");
#define nsamples 4096
short data[nsamples];
for (int i=0;imAudioDataByteSize = nsamples * sizeof(short) * 1;
assert(bufout->mAudioDataByteSize mAudioData, data, bufout->mAudioDataByteSize);
AudioQueueEnqueueBuffer(state.queue, bufout, 0, NULL);
}
void audio_init()
{
int i;
bzero(&state, sizeof(state));
state.desc.mFormatID = kAudioFormatLinearPCM;
state.desc.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
state.desc.mSampleRate = 44100;
state.desc.mChannelsPerFrame = 1;
state.desc.mFramesPerPacket = 1;
state.desc.mBytesPerFrame = sizeof (short) * state.desc.mChannelsPerFrame;
state.desc.mBytesPerPacket = state.desc.mBytesPerFrame;
state.desc.mBitsPerChannel = (state.desc.mBytesPerFrame*8)/state.desc.mChannelsPerFrame;
state.desc.mReserved = 0;
state.buffer_size = state.desc.mBytesPerFrame * state.desc.mSampleRate;
if (noErr != AudioQueueNewOutput(&state.desc, audio_callback, 0, NULL, NULL, 0, &state.queue)) {
printf("audioqueue error\n");
return;
}
// Start some empty playback so we'll get the callbacks that fill in the actual audio.
for (i = 0; i mAudioDataByteSize = state.buffer_size;
AudioQueueEnqueueBuffer(state.queue, state.buffers[i], 0, NULL);
}
if (noErr != AudioQueueStart(state.queue, NULL)) printf("AudioQueueStart failed\n");
printf("started audio\n");
}
int main() {
audio_init();
while (1) {
printf("I can't hear anything!\n");
}
}
REFS:
developer.apple.com Audio Queue Services Programming Guide: Playing Audio
developer.apple.com Audio Queue Services Reference
where to start with audio synthesis on iPhone Answer by Andy J Buchanan
Note that I had to explicitly set mAudioDataByteSize to the size I allocated.
In the docs they mention that it is initially set to zero, and that's what I found.
The docs don't say why but I suspect it's to allow for variable size buffers or something?
/* Ben's Audio Example for OSX 10.5+ (yeah Audio Queue)
Ben White, Nov, 2011
Makefile:
example: example.c
gcc -o $# $< -Wimplicit -framework AudioToolbox \
-framework CoreFoundation -lm
*/
#include "AudioToolbox/AudioToolbox.h"
typedef struct {
double phase, phase_inc;
int count;
} PhaseBlah;
void callback (void *ptr, AudioQueueRef queue, AudioQueueBufferRef buf_ref)
{
OSStatus status;
PhaseBlah *p = ptr;
AudioQueueBuffer *buf = buf_ref;
int nsamp = buf->mAudioDataByteSize / 2;
short *samp = buf->mAudioData;
int ii;
printf ("Callback! nsamp: %d\n", nsamp);
for (ii = 0; ii < nsamp; ii++) {
samp[ii] = (int) (30000.0 * sin(p->phase));
p->phase += p->phase_inc;
//printf("phase: %.3f\n", p->phase);
}
p->count++;
status = AudioQueueEnqueueBuffer (queue, buf_ref, 0, NULL);
printf ("Enqueue status: %d\n", status);
}
int main (int argc, char *argv[])
{
AudioQueueRef queue;
PhaseBlah phase = { 0, 2 * 3.14159265359 * 450 / 44100 };
OSStatus status;
AudioStreamBasicDescription fmt = { 0 };
AudioQueueBufferRef buf_ref, buf_ref2;
fmt.mSampleRate = 44100;
fmt.mFormatID = kAudioFormatLinearPCM;
fmt.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
fmt.mFramesPerPacket = 1;
fmt.mChannelsPerFrame = 1; // 2 for stereo
fmt.mBytesPerPacket = fmt.mBytesPerFrame = 2; // x2 for stereo
fmt.mBitsPerChannel = 16;
status = AudioQueueNewOutput(&fmt, callback, &phase, CFRunLoopGetCurrent(),
kCFRunLoopCommonModes, 0, &queue);
if (status == kAudioFormatUnsupportedDataFormatError) puts ("oops!");
else printf("NewOutput status: %d\n", status);
status = AudioQueueAllocateBuffer (queue, 20000, &buf_ref);
printf ("Allocate status: %d\n", status);
AudioQueueBuffer *buf = buf_ref;
printf ("buf: %p, data: %p, len: %d\n", buf, buf->mAudioData, buf->mAudioDataByteSize);
buf->mAudioDataByteSize = 20000;
callback (&phase, queue, buf_ref);
status = AudioQueueAllocateBuffer (queue, 20000, &buf_ref2);
printf ("Allocate2 status: %d\n", status);
buf = buf_ref2;
buf->mAudioDataByteSize = 20000;
callback (&phase, queue, buf_ref2);
status = AudioQueueSetParameter (queue, kAudioQueueParam_Volume, 1.0);
printf ("Volume status: %d\n", status);
status = AudioQueueStart (queue, NULL);
printf ("Start status: %d\n", status);
while (phase.count < 15)
CFRunLoopRunInMode (
kCFRunLoopDefaultMode,
0.25, // seconds
false // don't return after source handled
);
return 0;
}
Based somewhat on the answer by bw1024, I created this complete ogg vorbis player with libvorbisfile.
It expands on the previous answer, demonstrates how to use a function to fill in the audio buffers (as in, doesn't generate the sound by itself) and adds end-of-playback detection with an event listener callback.
The code itself is heavily commented, which hopefully explains everything that needs to be explained.
I tried to keep it as close to "production quality" of both Audio Queues and libvorbisfile, so it contains "real" error conditions and checks for exceptional circumstances; such as variable sample rate in the vorbis file, which it can't handle.
I hope none of the noise distracts from its value as a sample.
// vorplay.c - by Johann `Myrkraverk' Oskarsson <johann#myrkraverk.com>
// In the interest of example code, it's explicitly licensed under the
// WTFPL, see the bottom of the file or http://www.wtfpl.net/ for details.
#include <pthread.h> // For pthread_exit().
#include <vorbis/vorbisfile.h>
#include <AudioToolbox/AudioToolbox.h>
#include <stdio.h>
// This is a complete example of an Ogg Vorbis player based on the vorbisfile
// library and the audio queue API in OS X.
// It can be either taken as an example of how to use libvorbisfile, or
// audio queue programming.
// There are many "magic number" constants in the code, and understanding
// them requires looking up the relevant documentation. Some, such as
// the number of buffers in the audio queue and the size of each buffer
// are the result of experimentation. A "real application" may benefit
// from allowing the user to tweak these, in order to resolve audio stutters.
// Error handling is done very simply in order to focus on the example code
// while still resembling "production code." Here, we use the
// if ( status = foo() ) { ... }
// syntax for error checking. The assignment in if()s is not an error.
// If your compiler is complaining, you can use its equivalent of the
// GCC switch -Wno-parentheses to silence it.
// Assuming you'll want to use libvorbisfile from mac ports, you can
// compile it like this.
// gcc -c -I/opt/local/include \
// vorplay.c \
// -Wno-parentheses
// And link with
// gcc -o vorplay vorplay.o \
// -L/opt/local/lib -lvorbisfile \
// -framework AudioToolbox
// The start/stop listener...
void listener( void *vorbis, AudioQueueRef queue, AudioQueuePropertyID id )
{
// Here, we're only listening for start/stop, so don't need to check
// the id; it's always kAudioQueueProperty_IsRunning in our case.
UInt32 running = 0;
UInt32 size = sizeof running;
/* OggVorbis_File *vf = (OggVorbis_File *) vorbis; */
OSStatus status = -1;
if ( status = AudioQueueGetProperty( queue, id, &running, &size ) ) {
printf( "AudioQueueGetProperty status = %d; running = %d\n",
status, running );
exit( 1 );
}
if ( !running ) {
// In a "real example" we'd clean up the vf pointer with ov_clear() and
// the audio queue with AudioQueueDispose(); however, the latter is
// better not called from within the listener function, so we just
// exit normally.
exit( 0 );
// In a "real" application, we might signal the termination with
// a pthread condition variable, or something similar, instead;
// where the waiting thread would call AudioQueueDispose(). It is
// "safe" to call ov_clear() here, but there's no point.
}
}
// The audio queue callback...
void callback( void *vorbis, AudioQueueRef queue, AudioQueueBufferRef buffer )
{
OggVorbis_File *vf = (OggVorbis_File *) vorbis;
int section = 0;
OSStatus status = -1;
// The parameters here are congruent with our format specification for
// the audio queue. We read directly into the audio queue buffer.
long r = ov_read( vf, buffer->mAudioData, buffer->mAudioDataBytesCapacity,
0, 2, 1, &section );
// As an extra precaution, check if the current buffer is the same sample
// rate and channel number as the audio queue.
{
vorbis_info *vinfo = ov_info( vf, section );
if ( vinfo == NULL ) {
printf( "ov_info status = NULL\n" );
exit( 1 );
}
AudioStreamBasicDescription description;
UInt32 size = sizeof description;
if ( status = AudioQueueGetProperty( queue,
kAudioQueueProperty_StreamDescription,
&description,
&size ) ) {
printf( "AudioQueueGetProperty status = %d\n", status );
exit( 1 );
}
// If we were using some other kind of audio playback API, such as OSS4
// we could simply change the sample rate and channel number on the fly.
// However, with an audio queue, we'd have to use a different
// one, afaict; so we don't handle it at all in this example.
if ( vinfo->rate != description.mSampleRate ) {
printf( "We don't handle changes in sample rate.\n" );
exit( 1 );
}
if ( vinfo->channels != description.mChannelsPerFrame ) {
printf( "We don't handle changes in channel numbers.\n" );
exit( 1 );
}
}
// The real "callback"...
if ( r == 0 ) { // No more data, stop playing.
// Flush data, to make sure we play to the end.
if ( status = AudioQueueFlush( queue ) ) {
printf( "AudioQueueFlush status = %d\n", status );
exit( 1 );
}
// Stop asynchronously.
if ( status = AudioQueueStop( queue, false ) ) {
printf( "AudioQueueStop status = %d\n", status );
exit( 1 );
}
} else if ( r < 0 ) { // Some error?
printf( "ov_read status = %ld\n", r );
exit( 1 );
} else { // The normal course of action.
// ov_read() may not return exactly the number of bytes we requested.
// so we update the buffer size per call.
buffer->mAudioDataByteSize = r;
if ( status = AudioQueueEnqueueBuffer( queue, buffer, 0, 0 ) ) {
printf( "AudioQueueEnqueueBuffer status = %d, r = %ld\n", status, r );
exit( 1 );
}
}
}
int main( int argc, char *argv[] )
{
// The very simple command line argument check.
if ( argc != 2 ) {
printf( "Usage: vorplay <file>\n" );
exit( 1 );
}
FILE *file = fopen( argv[ 1 ], "r" );
if ( file == NULL ) {
printf( "Unable to open input file.\n" );
exit( 1 );
}
OggVorbis_File vf;
// Using OV_CALLBACKS_DEFAULT means ov_clear() will close the file.
// However, this particular example doesn't use that function.
// A typical place for it might be the listener(), when we stop
// playing.
if ( ov_open_callbacks( file, &vf, 0, 0, OV_CALLBACKS_DEFAULT ) ) {
printf( "ov_open_callbacks() failed. Not an Ogg Vorbis file?\n" );
exit( 1 );
}
// For the sample rate and channel number in the audio.
vorbis_info *vinfo = ov_info( &vf, -1 );
if ( vinfo == NULL ) {
printf( "ov_info status = NULL\n" );
exit( 1 );
}
// The audio queue format specification. This structure must be set
// to values congruent to the ones we use with ov_read().
AudioStreamBasicDescription format = { 0 };
// First, the constants. The format is quite hard coded, both here
// and in the calls to ov_read().
format.mFormatID = kAudioFormatLinearPCM;
format.mFormatFlags =
kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
format.mFramesPerPacket = 1;
format.mBitsPerChannel = 16;
// Load the sample rate and channel number from the vorbis file.
format.mSampleRate = vinfo->rate;
format.mChannelsPerFrame = vinfo->channels;
// The number of bytes depends on the channel number.
format.mBytesPerPacket =
format.mBytesPerFrame = 2 * vinfo->channels; // times two, for 16bit
OSStatus status = -1;
AudioQueueRef queue;
// Create the audio queue with the desired format. Notice that we
// use the OggVorbis_File pointer as the data far the callback.
if ( status = AudioQueueNewOutput( &format, callback,
&vf, NULL, NULL, 0, &queue ) ) {
printf( "AudioQueueNewOutput status = %d\n", status );
exit( 1 );
}
// For me distortions happen with 3 buffers; hence the magic number 5.
AudioQueueBufferRef buffers[ 5 ];
for ( int i = 0; i < sizeof buffers / sizeof (AudioQueueBufferRef); i++ ) {
// For each buffer...
// The size of the buffer is a magic number. 4096 is good enough, too.
if ( status = AudioQueueAllocateBuffer( queue, 8192, &buffers[ i ] ) ) {
printf( "AudioQueueAllocateBuffer status = %d\n", status );
exit( 1 );
}
// Enqueue buffers, before play. According to the process outlined
// in the Audio Queue Services Programming Guide, we must do this
// before calling AudioQueueStart() and it's simplest to do it like
// this.
callback( &vf, queue, buffers[ i ] );
}
// We set the volume to maximum; even though the docs say it's the
// default.
if ( status = AudioQueueSetParameter( queue,
kAudioQueueParam_Volume, 1.0 ) ) {
printf( "AudioQueueSetParameter status = %d\n", status );
exit( 1 );
}
// Here, we might want to call AudioQueuePrime if we were playing one
// of the supported compressed formats. However, since we only have
// raw PCM buffers to play, I don't see the point. Maybe playing will
// start faster with it, after AudioQueueStart() but I still don't see
// the point for this example; if there's a delay, it'll happen anyway.
// We add a listener for the start/stop event, so we know when to call
// exit( 0 ) and terminate the application. We also give it the vf
// pointer, even though it's not used in our listener().
if ( status = AudioQueueAddPropertyListener( queue,
kAudioQueueProperty_IsRunning,
listener,
&vf ) ) {
printf( "AudioQueueAddPropertyListener status = %d\n", status );
exit( 1 );
}
// And then start to play the file.
if ( status = AudioQueueStart( queue, 0 ) ) {
printf( "AudioQueueStart status = %d\n", status );
exit( 1 );
}
// Work's For Me[tm]. This trick to make sure the process doesn't
// terminate before the song has played "works for me" on
// OS X 10.10.3. If you're going to use this same trick in production
// code, you might as well turn off the joinability of the main thread,
// with pthread_detach() and also make sure no callback or listener is
// using data from the stack. Unlike this example.
pthread_exit( 0 );
return 0; // never reached, left intact in case some compiler complains.
}
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
// Version 2, December 2004
//
// Copyright (C) 2015 Johann `Myrkraverk' Oskarsson
// <johann#myrkraverk.com>
//
// Everyone is permitted to copy and distribute verbatim or modified
// copies of this license document, and changing it is allowed as long
// as the name is changed.
//
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
//
// 0. You just DO WHAT THE FUCK YOU WANT TO.