diff options
author | Arthur de Jong <arthur@arthurdejong.org> | 2009-05-29 21:22:58 +0000 |
---|---|---|
committer | Arthur de Jong <arthur@arthurdejong.org> | 2009-05-29 21:22:58 +0000 |
commit | f91b33e7b87b0447029e9b7593b796c3b9d0d0b0 (patch) | |
tree | cb66705231e6c4480c62c3b690867c426cf2763b /common | |
parent | 7c32d0121c95bbb05ab064b29bf7ec2ba5defc00 (diff) |
refactor protocol reading and writing macros to the common directory, use more logical names and in the PAM module no longer use NSS status codes (import of r887 from nss-pam-ldapd)
git-svn-id: http://arthurdejong.org/svn/nss-pam-ldapd/nss-ldapd@904 ef36b2f9-881f-0410-afb5-c4e39611909c
Diffstat (limited to 'common')
-rw-r--r-- | common/Makefile.am | 6 | ||||
-rw-r--r-- | common/nslcd-prot.c | 89 | ||||
-rw-r--r-- | common/nslcd-prot.h | 348 |
3 files changed, 442 insertions, 1 deletions
diff --git a/common/Makefile.am b/common/Makefile.am index a813da5..3a3b6d3 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -17,12 +17,16 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA -noinst_LIBRARIES = libtio.a libdict.a +noinst_LIBRARIES = libtio.a libprot.a libdict.a AM_CPPFLAGS=-I$(top_srcdir) AM_CFLAGS = -fPIC +EXTRA_DIST = nslcd-prot.h + libtio_a_SOURCES = tio.c tio.h +libprot_a_SOURCES = nslcd-prot.c nslcd-prot.h + libdict_a_SOURCES = dict.c dict.h \ set.c set.h diff --git a/common/nslcd-prot.c b/common/nslcd-prot.c new file mode 100644 index 0000000..102a6df --- /dev/null +++ b/common/nslcd-prot.c @@ -0,0 +1,89 @@ +/* + nslcd-prot.c - common functions for NSLCD lookups + + Copyright (C) 2006 West Consulting + Copyright (C) 2006, 2007, 2008 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "config.h" + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif /* HAVE_STDINT_H */ +#include <unistd.h> +#include <stdio.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <string.h> + +#include "nslcd.h" +#include "nslcd-prot.h" + +/* buffer sizes for I/O */ +#define READBUFFER_MINSIZE 1024 +#define READBUFFER_MAXSIZE 2*1024*1024 +#define WRITEBUFFER_MINSIZE 32 +#define WRITEBUFFER_MAXSIZE 32 + +/* Note that the READBUFFER_MAXSIZE should be large enough to hold any single + result entity as defined in nslcd.h because the get*ent() functions expect + to be able to tio_reset() the stream to re-read the current entity. + Since group entities can grow arbitrarily large, this setting limits the + number of users that can be put in a group. */ + +/* returns a socket to the server or NULL on error (see errno), + socket should be closed with fclose() */ +TFILE *nslcd_client_open() +{ + int sock; + struct sockaddr_un addr; + struct timeval readtimeout,writetimeout; + TFILE *fp; + /* create a socket */ + if ( (sock=socket(PF_UNIX,SOCK_STREAM,0))<0 ) + return NULL; + /* create socket address structure */ + memset(&addr,0,sizeof(struct sockaddr_un)); + addr.sun_family=AF_UNIX; + strncpy(addr.sun_path,NSLCD_SOCKET,sizeof(addr.sun_path)); + addr.sun_path[sizeof(addr.sun_path)-1]='\0'; + /* connect to the socket */ + if (connect(sock,(struct sockaddr *)&addr,(socklen_t)sizeof(struct sockaddr_un))<0) + { + (void)close(sock); + return NULL; + } + /* set the timeouts */ + readtimeout.tv_sec=60; /* looking up stuff may take some time */ + readtimeout.tv_usec=0; + writetimeout.tv_sec=10; /* nslcd could be loaded with requests */ + writetimeout.tv_usec=0; + /* create a stream object */ + if ((fp=tio_fdopen(sock,&readtimeout,&writetimeout, + READBUFFER_MINSIZE,READBUFFER_MAXSIZE, + WRITEBUFFER_MINSIZE,WRITEBUFFER_MAXSIZE))==NULL) + { + (void)close(sock); + return NULL; + } + /* return the stream */ + return fp; +} diff --git a/common/nslcd-prot.h b/common/nslcd-prot.h new file mode 100644 index 0000000..bab5596 --- /dev/null +++ b/common/nslcd-prot.h @@ -0,0 +1,348 @@ +/* + nslcd-prot.h - helper macros for reading and writing in protocol streams + + Copyright (C) 2006 West Consulting + Copyright (C) 2006, 2007, 2009 Arthur de Jong + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#ifndef _NSLCD_PROT_H +#define _NSLCD_PROT_H 1 + +#include "tio.h" + +/* If you use these macros you should define the following macros to + handle error conditions (these marcos should clean up and return from the + function): + ERROR_OUT_WRITEERROR(fp) + ERROR_OUT_READERROR(fp) + ERROR_OUT_BUFERROR(fp) + ERROR_OUT_NOSUCCESS(fp) */ + + +/* Debugging marcos that can be used to enable detailed protocol logging, + pass -DDEBUG_PROT to do overall protocol debugging, and -DDEBUG_PROT_DUMP + to dump the actual bytestream. */ + +#ifdef DEBUG_PROT +/* define a debugging macro to output logging */ +#include <string.h> +#include <errno.h> +#define DEBUG_PRINT(fmt,arg) \ + fprintf(stderr,"%s:%d:%s: " fmt "\n",__FILE__,__LINE__,__PRETTY_FUNCTION__,arg); +#else /* DEBUG_PROT */ +/* define an empty debug macro to disable logging */ +#define DEBUG_PRINT(fmt,arg) +#endif /* not DEBUG_PROT */ + +#ifdef DEBUG_PROT_DUMP +/* define a debugging macro to output detailed logging */ +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif /* HAVE_STDINT_H */ +static void debug_dump(const void *ptr,size_t size) +{ + int i; + for (i=0;i<size;i++) + fprintf(stderr," %02x",((const uint8_t *)ptr)[i]); + fprintf(stderr,"\n"); +} +#define DEBUG_DUMP(ptr,size) \ + fprintf(stderr,"%s:%d:%s:",__FILE__,__LINE__,__PRETTY_FUNCTION__); \ + debug_dump(ptr,size); +#else /* DEBUG_PROT_DUMP */ +/* define an empty debug macro to disable logging */ +#define DEBUG_DUMP(ptr,size) +#endif /* not DEBUG_PROT_DUMP */ + + +/* WRITE marcos, used for writing data, on write error they will + call the ERROR_OUT_WRITEERROR macro + these macros may require the availability of the following + variables: + int32_t tmpint32; - temporary variable + */ + +#define WRITE(fp,ptr,size) \ + DEBUG_PRINT("WRITE : var="__STRING(ptr)" size=%d",(int)size); \ + DEBUG_DUMP(ptr,size); \ + if (tio_write(fp,ptr,(size_t)size)) \ + { \ + DEBUG_PRINT("WRITE : var="__STRING(ptr)" error: %s",strerror(errno)); \ + ERROR_OUT_WRITEERROR(fp); \ + } + +#define WRITE_TYPE(fp,field,type) \ + WRITE(fp,&(field),sizeof(type)) + +#define WRITE_INT32(fp,i) \ + DEBUG_PRINT("WRITE_INT32 : var="__STRING(i)" int32=%d",(int)i); \ + tmpint32=(int32_t)(i); \ + WRITE_TYPE(fp,tmpint32,int32_t) + +#define WRITE_STRING(fp,str) \ + DEBUG_PRINT("WRITE_STRING: var="__STRING(str)" string=\"%s\"",str); \ + if (str==NULL) \ + { \ + WRITE_INT32(fp,0); \ + } \ + else \ + { \ + WRITE_INT32(fp,strlen(str)); \ + if (tmpint32>0) \ + { WRITE(fp,str,tmpint32); } \ + } + +#define WRITE_STRINGLIST(fp,arr) \ + /* first determin length of array */ \ + for (tmp3int32=0;(arr)[tmp3int32]!=NULL;tmp3int32++) \ + /*noting*/ ; \ + /* write number of strings */ \ + DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d",(int)tmp3int32); \ + WRITE_TYPE(fp,tmp3int32,int32_t); \ + /* write strings */ \ + for (tmp2int32=0;tmp2int32<tmp3int32;tmp2int32++) \ + { \ + WRITE_STRING(fp,(arr)[tmp2int32]); \ + } + +#define WRITE_STRINGLIST_EXCEPT(fp,arr,not) \ + /* first determin length of array */ \ + tmp3int32=0; \ + for (tmp2int32=0;(arr)[tmp2int32]!=NULL;tmp2int32++) \ + if (strcmp((arr)[tmp2int32],(not))!=0) \ + tmp3int32++; \ + /* write number of strings (mius one because we intend to skip one) */ \ + DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d",(int)tmp3int32); \ + WRITE_TYPE(fp,tmp3int32,int32_t); \ + /* write strings */ \ + for (tmp2int32=0;(arr)[tmp2int32]!=NULL;tmp2int32++) \ + { \ + if (strcmp((arr)[tmp2int32],(not))!=0) \ + { \ + WRITE_STRING(fp,(arr)[tmp2int32]); \ + } \ + } + + +/* READ macros, used for reading data, on read error they will + call the ERROR_OUT_READERROR or ERROR_OUT_BUFERROR macro + these macros may require the availability of the following + variables: + int32_t tmpint32; - temporary variable + */ + +#define READ(fp,ptr,size) \ + if (tio_read(fp,ptr,(size_t)size)) \ + { \ + DEBUG_PRINT("READ : var="__STRING(ptr)" error: %s",strerror(errno)); \ + ERROR_OUT_READERROR(fp); \ + } \ + DEBUG_PRINT("READ : var="__STRING(ptr)" size=%d",(int)size); \ + DEBUG_DUMP(ptr,size); + +#define READ_TYPE(fp,field,type) \ + READ(fp,&(field),sizeof(type)) + +#define READ_INT32(fp,i) \ + READ_TYPE(fp,tmpint32,int32_t); \ + i=tmpint32; \ + DEBUG_PRINT("READ_INT32 : var="__STRING(i)" int32=%d",(int)i); + +/* read a string in a fixed-size "normal" buffer */ +#define READ_STRING(fp,buffer) \ + /* read the size of the string */ \ + READ_TYPE(fp,tmpint32,int32_t); \ + DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" strlen=%d",tmpint32); \ + /* check if read would fit */ \ + if (((size_t)tmpint32)>=sizeof(buffer)) \ + { \ + /* will not fit */ \ + DEBUG_PRINT("READ : buffer error: %d bytes too large",(tmpint32-sizeof(buffer))+1); \ + ERROR_OUT_BUFERROR(fp); \ + } \ + /* read string from the stream */ \ + if (tmpint32>0) \ + { READ(fp,buffer,(size_t)tmpint32); } \ + /* null-terminate string in buffer */ \ + buffer[tmpint32]='\0'; \ + DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" string=\"%s\"",buffer); + + +/* READ BUF macros that read data into a pre-allocated buffer. + these macros may require the availability of the following + variables: + int32_t tmpint32; - temporary variable + char *buffer; - pointer to a buffer for reading strings + size_t buflen; - the size of the buffer + size_t bufptr; - the current position in the buffer + */ + +/* current position in the buffer */ +#define BUF_CUR \ + (buffer+bufptr) + +/* check that the buffer has sz bytes left in it */ +#define BUF_CHECK(fp,sz) \ + if ((bufptr+(size_t)(sz))>buflen) \ + { \ + /* will not fit */ \ + DEBUG_PRINT("READ : buffer error: %d bytes too small",(bufptr+(sz)-(buflen))); \ + ERROR_OUT_BUFERROR(fp); \ + } + +/* move the buffer pointer */ +#define BUF_SKIP(sz) \ + bufptr+=(size_t)(sz); + +/* move BUF_CUR foreward so that it is aligned to the specified + type width */ +#define BUF_ALIGN(fp,type) \ + /* figure out number of bytes to skip foreward */ \ + tmp2int32=(sizeof(type)-((BUF_CUR-(char *)NULL)%sizeof(type)))%sizeof(type); \ + /* check and skip */ \ + BUF_CHECK(fp,tmp2int32); \ + BUF_SKIP(tmp2int32); + +/* allocate a piece of the buffer to store an array in */ +#define BUF_ALLOC(fp,ptr,type,num) \ + /* align to the specified type width */ \ + BUF_ALIGN(fp,type); \ + /* check that we have enough room */ \ + BUF_CHECK(fp,(size_t)(num)*sizeof(type)); \ + /* store the pointer */ \ + (ptr)=(type *)BUF_CUR; \ + /* reserve the space */ \ + BUF_SKIP((size_t)(num)*sizeof(type)); + +/* read a binary blob into the buffer */ +#define READ_BUF(fp,ptr,sz) \ + /* check that there is enough room and read */ \ + BUF_CHECK(fp,sz); \ + READ(fp,BUF_CUR,(size_t)sz); \ + /* store pointer and skip */ \ + (ptr)=BUF_CUR; \ + BUF_SKIP(sz); + +/* read string in the buffer (using buffer, buflen and bufptr) + and store the actual location of the string in field */ +#define READ_BUF_STRING(fp,field) \ + /* read the size of the string */ \ + READ_TYPE(fp,tmpint32,int32_t); \ + DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" strlen=%d",tmpint32); \ + /* check if read would fit */ \ + BUF_CHECK(fp,tmpint32+1); \ + /* read string from the stream */ \ + if (tmpint32>0) \ + { READ(fp,BUF_CUR,(size_t)tmpint32); } \ + /* null-terminate string in buffer */ \ + BUF_CUR[tmpint32]='\0'; \ + DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" string=\"%s\"",BUF_CUR); \ + /* prepare result */ \ + (field)=BUF_CUR; \ + BUF_SKIP(tmpint32+1); + +/* read an array from a stram and store it as a null-terminated + array list (size for the array is allocated) */ +#define READ_BUF_STRINGLIST(fp,arr) \ + /* read the number of entries */ \ + READ_TYPE(fp,tmp3int32,int32_t); \ + DEBUG_PRINT("READ_STRLST: var="__STRING(arr)" num=%d",(int)tmp3int32); \ + /* allocate room for *char[num+1] */ \ + BUF_ALLOC(fp,arr,char *,tmp3int32+1); \ + /* read all entries */ \ + for (tmp2int32=0;tmp2int32<tmp3int32;tmp2int32++) \ + { \ + READ_BUF_STRING(fp,(arr)[tmp2int32]); \ + } \ + /* set last entry to NULL */ \ + (arr)[tmp2int32]=NULL; + + +/* SKIP macros for skipping over certain parts of the protocol stream. */ + +/* skip a number of bytes foreward */ +#define SKIP(fp,sz) \ + DEBUG_PRINT("READ : skip %d bytes",(int)(sz)); \ + /* read (skip) the specified number of bytes */ \ + if (tio_skip(fp,sz)) \ + { \ + DEBUG_PRINT("READ : skip error: %s",strerror(errno)); \ + ERROR_OUT_READERROR(fp); \ + } + +/* read a string from the stream but don't do anything with the result */ +#define SKIP_STRING(fp) \ + /* read the size of the string */ \ + READ_TYPE(fp,tmpint32,int32_t); \ + DEBUG_PRINT("READ_STRING: skip %d bytes",(int)tmpint32); \ + /* read (skip) the specified number of bytes */ \ + SKIP(fp,tmpint32); + +/* skip a list of strings */ +#define SKIP_STRINGLIST(fp) \ + /* read the number of entries */ \ + READ_TYPE(fp,tmp3int32,int32_t); \ + DEBUG_PRINT("READ_STRLST: skip %d strings",(int)tmp3int32); \ + /* read all entries */ \ + for (tmp2int32=0;tmp2int32<tmp3int32;tmp2int32++) \ + { \ + SKIP_STRING(fp); \ + } + + +/* These are functions and macors for performing common operations in + the nslcd request/response protocol. */ + +/* returns a socket to the server or NULL on error (see errno), + socket should be closed with tio_close() */ +TFILE *nslcd_client_open(void) + MUST_USE; + +/* generic request code */ +#define NSLCD_REQUEST(fp,action,writefn) \ + /* open a client socket */ \ + if ((fp=nslcd_client_open())==NULL) \ + { ERROR_OUT_OPENERROR } \ + /* write a request header with a request code */ \ + WRITE_INT32(fp,(int32_t)NSLCD_VERSION) \ + WRITE_INT32(fp,(int32_t)action) \ + /* write the request parameters (if any) */ \ + writefn; \ + /* flush the stream */ \ + if (tio_flush(fp)<0) \ + { \ + DEBUG_PRINT("WRITE_FLUSH : error: %s",strerror(errno)); \ + ERROR_OUT_WRITEERROR(fp); \ + } \ + /* read and check response version number */ \ + READ_TYPE(fp,tmpint32,int32_t); \ + if (tmpint32!=(int32_t)NSLCD_VERSION) \ + { ERROR_OUT_READERROR(fp) } \ + /* read and check response request number */ \ + READ_TYPE(fp,tmpint32,int32_t); \ + if (tmpint32!=(int32_t)(action)) \ + { ERROR_OUT_READERROR(fp) } + +/* Read the response code (the result code of the query) from + the stream. */ +#define READ_RESPONSE_CODE(fp) \ + READ_TYPE(fp,tmpint32,int32_t); \ + if (tmpint32!=(int32_t)NSLCD_RESULT_BEGIN) \ + { ERROR_OUT_NOSUCCESS(fp) } + +#endif /* not _NSLCD_PROT_H */ |