Ported over atftpd from Linux - should work on BSD as well, now.

Not yet tested, and it will probably not work correctly until we support
IP_RECVDSTADDR correctly (we don't support that CMSG stuff yet).


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22171 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2007-09-04 21:35:58 +00:00
parent 35f57d15ff
commit ef27f7d0e0
25 changed files with 6405 additions and 0 deletions

View File

@ -1,6 +1,7 @@
SubDir HAIKU_TOP src bin network ; SubDir HAIKU_TOP src bin network ;
SubInclude HAIKU_TOP src bin network arp ; SubInclude HAIKU_TOP src bin network arp ;
SubInclude HAIKU_TOP src bin network atftpd ;
SubInclude HAIKU_TOP src bin network ftp ; SubInclude HAIKU_TOP src bin network ftp ;
SubInclude HAIKU_TOP src bin network ifconfig ; SubInclude HAIKU_TOP src bin network ifconfig ;
SubInclude HAIKU_TOP src bin network login ; SubInclude HAIKU_TOP src bin network login ;

View File

@ -0,0 +1,19 @@
SubDir HAIKU_TOP src bin network atftpd ;
UseHeaders [ FDirName $(HAIKU_TOP) headers compatibility bsd ] : true ;
BinCommand atftpd :
argz.c
logger.c
options.c
stats.c
tftp_def.c
tftp_io.c
tftpd.c
tftpd_file.c
tftpd_list.c
tftpd_mcast.c
tftpd_mtftp.c
tftpd_pcre.c
: libbsd.so $(TARGET_NETWORK_LIBS)
;

View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View File

@ -0,0 +1,132 @@
/*
* argz.h
* The functions below are "borrowed" from glibc-2.2.3 (argz-next.c).
* This has been done to make atftp compile with uclibc, BSD, Solaris
* and other platform without glic
*
*
* $Id: argz.c,v 1.1 2003/01/21 01:38:35 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef HAVE_ARGZ
/* Routines for dealing with '\0' separated arg vectors.
Copyright (C) 1995, 96, 97, 98, 99, 2000 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C 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.
The GNU C 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 the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#include <stdlib.h>
#include "argz.h"
char * argz_next (const char *argz, size_t argz_len, const char *entry)
{
if (entry)
{
if (entry < argz + argz_len)
entry = strchr (entry, '\0') + 1;
return entry >= argz + argz_len ? NULL : (char *) entry;
}
else
if (argz_len > 0)
return (char *) argz;
else
return NULL;
}
error_t argz_create_sep (const char *string, int delim, char **argz, size_t *len)
{
size_t nlen = strlen (string) + 1;
if (nlen > 1)
{
const char *rp;
char *wp;
*argz = (char *) malloc (nlen);
if (*argz == NULL)
return ENOMEM;
rp = string;
wp = *argz;
do
if (*rp == delim)
{
if (wp > *argz && wp[-1] != '\0')
*wp++ = '\0';
else
--nlen;
}
else
*wp++ = *rp;
while (*rp++ != '\0');
if (nlen == 0)
{
free (*argz);
*argz = NULL;
*len = 0;
}
*len = nlen;
}
else
{
*argz = NULL;
*len = 0;
}
return 0;
}
size_t argz_count (const char *argz, size_t len)
{
size_t count = 0;
while (len > 0)
{
size_t part_len = strlen(argz);
argz += part_len + 1;
len -= part_len + 1;
count++;
}
return count;
}
/* Puts pointers to each string in ARGZ, plus a terminating 0 element, into
ARGV, which must be large enough to hold them all. */
void argz_extract (const char *argz, size_t len, char **argv)
{
while (len > 0)
{
size_t part_len = strlen (argz);
*argv++ = (char *) argz;
argz += part_len + 1;
len -= part_len + 1;
}
*argv = 0;
}
#endif

View File

@ -0,0 +1,211 @@
/*
* argz.h
* Source file borrowed from glibc-2.2.3. This has been done to make
* atftp compile with uclibc, BSD, Solaris and other platform without
* glic
*
* $Id: argz.h,v 1.1 2003/01/21 01:38:35 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
/* Routines for dealing with '\0' separated arg vectors.
Copyright (C) 1995, 96, 97, 98, 99, 2000 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C 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.
The GNU C 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 the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
#ifndef _ARGZ_H
#define _ARGZ_H 1
//#include <features.h>
#include <sys/cdefs.h>
#define __need_error_t
#include <errno.h>
#include <string.h> /* Need size_t, and strchr is called below. */
#ifndef __const
# define __const const
#endif
#ifndef __error_t_defined
typedef int error_t;
#endif
#define __THROW
#define __restrict
#define __attribute_pure__
#define __BEGIN_DECLS
__BEGIN_DECLS
/* Make a '\0' separated arg vector from a unix argv vector, returning it in
ARGZ, and the total length in LEN. If a memory allocation error occurs,
ENOMEM is returned, otherwise 0. The result can be destroyed using free. */
extern error_t __argz_create (char *__const __argv[], char **__restrict __argz,
size_t *__restrict __len) __THROW;
extern error_t argz_create (char *__const __argv[], char **__restrict __argz,
size_t *__restrict __len) __THROW;
/* Make a '\0' separated arg vector from a SEP separated list in
STRING, returning it in ARGZ, and the total length in LEN. If a
memory allocation error occurs, ENOMEM is returned, otherwise 0.
The result can be destroyed using free. */
extern error_t __argz_create_sep (__const char *__restrict __string,
int __sep, char **__restrict __argz,
size_t *__restrict __len) __THROW;
extern error_t argz_create_sep (__const char *__restrict __string,
int __sep, char **__restrict __argz,
size_t *__restrict __len) __THROW;
/* Returns the number of strings in ARGZ. */
extern size_t __argz_count (__const char *__argz, size_t __len)
__THROW __attribute_pure__;
extern size_t argz_count (__const char *__argz, size_t __len)
__THROW __attribute_pure__;
/* Puts pointers to each string in ARGZ into ARGV, which must be large enough
to hold them all. */
extern void __argz_extract (__const char *__restrict __argz, size_t __len,
char **__restrict __argv) __THROW;
extern void argz_extract (__const char *__restrict __argz, size_t __len,
char **__restrict __argv) __THROW;
/* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
except the last into the character SEP. */
extern void __argz_stringify (char *__argz, size_t __len, int __sep) __THROW;
extern void argz_stringify (char *__argz, size_t __len, int __sep) __THROW;
/* Append BUF, of length BUF_LEN to the argz vector in ARGZ & ARGZ_LEN. */
extern error_t __argz_append (char **__restrict __argz,
size_t *__restrict __argz_len,
__const char *__restrict __buf, size_t _buf_len)
__THROW;
extern error_t argz_append (char **__restrict __argz,
size_t *__restrict __argz_len,
__const char *__restrict __buf, size_t __buf_len)
__THROW;
/* Append STR to the argz vector in ARGZ & ARGZ_LEN. */
extern error_t __argz_add (char **__restrict __argz,
size_t *__restrict __argz_len,
__const char *__restrict __str) __THROW;
extern error_t argz_add (char **__restrict __argz,
size_t *__restrict __argz_len,
__const char *__restrict __str) __THROW;
/* Append SEP separated list in STRING to the argz vector in ARGZ &
ARGZ_LEN. */
extern error_t __argz_add_sep (char **__restrict __argz,
size_t *__restrict __argz_len,
__const char *__restrict __string, int __delim)
__THROW;
extern error_t argz_add_sep (char **__restrict __argz,
size_t *__restrict __argz_len,
__const char *__restrict __string, int __delim)
__THROW;
/* Delete ENTRY from ARGZ & ARGZ_LEN, if it appears there. */
extern void __argz_delete (char **__restrict __argz,
size_t *__restrict __argz_len,
char *__restrict __entry) __THROW;
extern void argz_delete (char **__restrict __argz,
size_t *__restrict __argz_len,
char *__restrict __entry) __THROW;
/* Insert ENTRY into ARGZ & ARGZ_LEN before BEFORE, which should be an
existing entry in ARGZ; if BEFORE is NULL, ENTRY is appended to the end.
Since ARGZ's first entry is the same as ARGZ, argz_insert (ARGZ, ARGZ_LEN,
ARGZ, ENTRY) will insert ENTRY at the beginning of ARGZ. If BEFORE is not
in ARGZ, EINVAL is returned, else if memory can't be allocated for the new
ARGZ, ENOMEM is returned, else 0. */
extern error_t __argz_insert (char **__restrict __argz,
size_t *__restrict __argz_len,
char *__restrict __before,
__const char *__restrict __entry) __THROW;
extern error_t argz_insert (char **__restrict __argz,
size_t *__restrict __argz_len,
char *__restrict __before,
__const char *__restrict __entry) __THROW;
/* Replace any occurrences of the string STR in ARGZ with WITH, reallocating
ARGZ as necessary. If REPLACE_COUNT is non-zero, *REPLACE_COUNT will be
incremented by number of replacements performed. */
extern error_t __argz_replace (char **__restrict __argz,
size_t *__restrict __argz_len,
__const char *__restrict __str,
__const char *__restrict __with,
unsigned int *__restrict __replace_count);
extern error_t argz_replace (char **__restrict __argz,
size_t *__restrict __argz_len,
__const char *__restrict __str,
__const char *__restrict __with,
unsigned int *__restrict __replace_count);
/* Returns the next entry in ARGZ & ARGZ_LEN after ENTRY, or NULL if there
are no more. If entry is NULL, then the first entry is returned. This
behavior allows two convenient iteration styles:
char *entry = 0;
while ((entry = argz_next (argz, argz_len, entry)))
...;
or
char *entry;
for (entry = argz; entry; entry = argz_next (argz, argz_len, entry))
...;
*/
extern char *__argz_next (__const char *__restrict __argz, size_t __argz_len,
__const char *__restrict __entry) __THROW;
extern char *argz_next (__const char *__restrict __argz, size_t __argz_len,
__const char *__restrict __entry) __THROW;
#ifdef __USE_EXTERN_INLINES
extern inline char *
__argz_next (__const char *__argz, size_t __argz_len,
__const char *__entry) __THROW
{
if (__entry)
{
if (__entry < __argz + __argz_len)
__entry = strchr (__entry, '\0') + 1;
return __entry >= __argz + __argz_len ? (char *) NULL : (char *) __entry;
}
else
return __argz_len > 0 ? (char *) __argz : 0;
}
extern inline char *
argz_next (__const char *__argz, size_t __argz_len,
__const char *__entry) __THROW
{
return __argz_next (__argz, __argz_len, __entry);
}
#endif /* Use extern inlines. */
__END_DECLS
#endif /* argz.h */

View File

@ -0,0 +1,7 @@
#include <pthread.h>
#define HAVE_MTFTP 1
//#undef HAVE_PCRE
//#undef HAVE_WRAP
#define VERSION "0.7"

View File

@ -0,0 +1,137 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* logger.c
* functions for logging messages.
*
* $Id: logger.c,v 1.12 2004/02/27 02:05:26 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <netdb.h>
#include "logger.h"
#define MAXLEN 128
static int log_syslog_is_open = 0;
static int log_priority = 0;
static char *log_filename = NULL;
static int log_fd;
static FILE *log_fp = NULL;
static char *log_ident;
/*
* Open a file for logging. If filename is NULL, then use
* stderr for the client or the syslog for the server. Log
* only message less or equal to priority.
*/
void open_logger(char *ident, char *filename, int priority)
{
close_logger(); /* make sure we initialise variables and close
previously opened log. */
log_priority = priority;
if (ident)
log_ident = strdup(ident);
else
log_ident = "unset";
if (filename)
log_filename = strdup(filename);
else
{
openlog(log_ident, LOG_PID, LOG_DAEMON);
log_syslog_is_open = 1;
}
if (log_filename)
{
if ((log_fd = open(log_filename, O_WRONLY | O_APPEND)) < 0)
{
openlog(log_ident, LOG_PID, LOG_DAEMON);
log_syslog_is_open = 1;
logger(LOG_CRIT, "Unable to open %s for logging, "
"reverting to syslog", log_filename);
}
else
log_fp = fdopen(log_fd, "a");
}
}
/*
* Same as syslog but allow to format a string, like printf, when logging to
* file. This fonction will either call syslog or fprintf depending of the
* previous call to open_logger().
*/
void logger(int severity, const char *fmt, ...)
{
char message[MAXLEN];
char time_buf[MAXLEN];
char hostname[MAXLEN];
time_t t;
struct tm *tm;
va_list args;
va_start(args, fmt);
time(&t);
tm = localtime(&t);
strftime(time_buf, MAXLEN, "%b %d %H:%M:%S", tm);
gethostname(hostname, MAXLEN);
if (severity <= log_priority)
{
vsnprintf(message, sizeof(message), fmt, args);
if (log_fp)
{
fprintf(log_fp, "%s %s %s[%d.%d]: %s\n", time_buf, hostname,
log_ident, getpid(), pthread_self(), message);
fflush(log_fp);
}
else if (log_syslog_is_open)
syslog(severity, "%s", message);
else
fprintf(stderr, "%s %s %s[%d.%d]: %s\n", time_buf, hostname,
log_ident, getpid(), pthread_self(), message);
}
va_end(args);
}
/*
* Close the file or syslog. Initialise variables.
*/
void close_logger(void)
{
log_priority = 0;
if (log_syslog_is_open)
closelog();
log_syslog_is_open = 0;
if (log_fp)
fclose(log_fp);
log_fp = NULL;
if (log_filename)
free(log_filename);
log_filename = NULL;
if (log_ident)
free(log_ident);
}

View File

@ -0,0 +1,26 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* logger.h
*
* $Id: logger.h,v 1.6 2000/12/27 00:57:16 remi Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef logger_h
#define logger_h
#include <syslog.h>
void open_logger(char *ident, char *filename, int priority);
void logger(int severity, const char *fmt, ...);
void close_logger(void);
#endif

View File

@ -0,0 +1,356 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* options.c
* Set of functions to deal with the options structure and for parsing
* options in TFTP data buffer.
*
* $Id: options.c,v 1.16 2003/04/25 00:16:18 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#if HAVE_ARGZ
#include <argz.h>
#else
#include "argz.h"
#endif
#include <arpa/tftp.h>
#include <string.h>
#include "options.h"
/*
* Fill a structure with the request packet of the client.
*/
int opt_parse_request(char *data, int data_size, struct tftp_opt *options)
{
char *entry = NULL;
char *tmp;
struct tftphdr *tftp_data = (struct tftphdr *)data;
size_t size = data_size - sizeof(tftp_data->th_opcode);
/* read filename */
entry = argz_next(tftp_data->th_stuff, size, entry);
if (!entry)
return ERR;
else
opt_set_options(options, "filename", entry);
/* read mode */
entry = argz_next(tftp_data->th_stuff, size, entry);
if (!entry)
return ERR;
else
opt_set_options(options, "mode", entry);
/* scan for options */
// FIXME: we should use opt_parse_options() here
while ((entry = argz_next(tftp_data->th_stuff, size, entry)))
{
tmp = entry;
entry = argz_next(tftp_data->th_stuff, size, entry);
if (!entry)
return ERR;
else
opt_set_options(options, tmp, entry);
}
return OK;
}
/*
* Fill a structure looking only at TFTP options.
*/
int opt_parse_options(char *data, int data_size, struct tftp_opt *options)
{
char *entry = NULL;
char *tmp;
struct tftphdr *tftp_data = (struct tftphdr *)data;
size_t size = data_size - sizeof(tftp_data->th_opcode);
while ((entry = argz_next(tftp_data->th_stuff, size, entry)))
{
tmp = entry;
entry = argz_next(tftp_data->th_stuff, size, entry);
if (!entry)
return ERR;
else
opt_set_options(options, tmp, entry);
}
return OK;
}
/*
* Set an option by name in the structure.
* name is the name of the option as in tftp_def.c.
* name is it's new value, that must comply with the rfc's.
* When setting an option, it is marked as specified.
*
*/
int opt_set_options(struct tftp_opt *options, char *name, char *value)
{
int i;
for (i = 0; i < OPT_NUMBER; i++)
{
if (strncasecmp(name, options[i].option, OPT_SIZE) == 0)
{
options[i].specified = 1;
if (value)
Strncpy(options[i].value, value, VAL_SIZE);
else
Strncpy(options[i].value, tftp_default_options[i].value,
VAL_SIZE);
return OK;
}
}
return ERR;
}
/*
* Return "value" for a given option name in the given option
* structure.
*/
int opt_get_options(struct tftp_opt *options, char *name, char *value)
{
int i;
for (i = 0; i < OPT_NUMBER; i++)
{
if (strncasecmp(name, options[i].option, OPT_SIZE) == 0)
{
if (options[i].enabled)
Strncpy(value, options[i].value, VAL_SIZE);
else
return ERR;
return OK;
}
}
return ERR;
}
/*
* Disable an option by name.
*/
int opt_disable_options(struct tftp_opt *options, char *name)
{
int i;
for (i = 2; i < OPT_NUMBER; i++)
{
if (name == NULL)
options[i].specified = 0;
else
{
if (strncasecmp(name, options[i].option, OPT_SIZE) == 0)
{
options[i].specified = 0;
return OK;
}
}
}
if (name == NULL)
return OK;
return ERR;
}
/*
* Return 1 if one or more options are specified in the options structure.
*/
int opt_support_options(struct tftp_opt *options)
{
int i;
int support = 0;
for (i = 2; i < OPT_NUMBER; i++)
{
if (options[i].specified)
support = 1;
}
return support;
}
/*
* The next few functions deal with TFTP options. Function's name are self
* explicative.
*/
int opt_get_tsize(struct tftp_opt *options)
{
int tsize;
if (options[OPT_TSIZE].enabled && options[OPT_TSIZE].specified)
{
tsize = atoi(options[OPT_TSIZE].value);
return tsize;
}
return ERR;
}
int opt_get_timeout(struct tftp_opt *options)
{
int timeout;
if (options[OPT_TIMEOUT].enabled && options[OPT_TIMEOUT].specified)
{
timeout = atoi(options[OPT_TIMEOUT].value);
return timeout;
}
return ERR;
}
int opt_get_blksize(struct tftp_opt *options)
{
int blksize;
if (options[OPT_BLKSIZE].enabled && options[OPT_BLKSIZE].specified)
{
blksize = atoi(options[OPT_BLKSIZE].value);
return blksize;
}
return ERR;
}
int opt_get_multicast(struct tftp_opt *options, char *addr, int *port, int *mc)
{
char *token = NULL;
char *string = NULL;
char *temp = NULL;
if (options[OPT_MULTICAST].enabled && options[OPT_MULTICAST].specified)
{
string = strdup(options[OPT_MULTICAST].value);
/* get first argument */
if ((token = strtok_r(string, ",", &temp)) == NULL)
{
free(string);
return ERR;
}
else
Strncpy(addr, token, IPADDRLEN);
/* get second argument */
if ((token = strtok_r(NULL, ",", &temp)) == NULL)
{
free(string);
return ERR;
}
else
{
*port = atoi(token);
if ((*port < 0) || (*port > 65536))
{
free(string);
return ERR;
}
}
/* get third (last) argument */
if ((token = strtok_r(NULL, ",", &temp)) == NULL)
{
free(string);
return ERR;
}
else
{
*mc = atoi(token);
if ((*mc != 0) && (*mc != 1))
{
free(string);
return ERR;
}
}
free(string);
return *mc;
}
return ERR;
}
void opt_set_tsize(int tsize, struct tftp_opt *options)
{
snprintf(options[OPT_TSIZE].value, VAL_SIZE, "%d", tsize);
}
void opt_set_timeout(int timeout, struct tftp_opt *options)
{
snprintf(options[OPT_TIMEOUT].value, VAL_SIZE, "%d", timeout);
}
void opt_set_blksize(int blksize, struct tftp_opt *options)
{
snprintf(options[OPT_BLKSIZE].value, VAL_SIZE, "%d", blksize);
}
void opt_set_multicast(struct tftp_opt *options, char *addr, int port, int mc)
{
snprintf(options[OPT_MULTICAST].value, VAL_SIZE, "%s,%d,%d", addr, port,
mc);
}
/*
* Format the content of the options structure (this is the content of a
* read or write request) to a string.
*/
void opt_request_to_string(struct tftp_opt *options, char *string, int len)
{
int i, index = 0;
for (i = 0; i < 2; i++)
{
if ((index + strlen(options[i].option) + 2) < len)
{
Strncpy(string + index, options[i].option, len - index);
index += strlen(options[i].option);
Strncpy(string + index, ": ", len - index);
index += 2;
}
if ((index + strlen(options[i].value) + 2) < len)
{
Strncpy(string + index, options[i].value, len - index);
index += strlen(options[i].value);
Strncpy(string + index, ", ", len - index);
index += 2;
}
}
opt_options_to_string(options, string + index, len - index);
}
/*
* Convert the options structure to a string.
*/
void opt_options_to_string(struct tftp_opt *options, char *string, int len)
{
int i, index = 0;
for (i = 2; i < OPT_NUMBER; i++)
{
if (options[i].specified && options[i].enabled)
{
if ((index + strlen(options[i].option) + 2) < len)
{
Strncpy(string + index, options[i].option, len - index);
index += strlen(options[i].option);
Strncpy(string + index, ": ", len - index);
index += 2;
}
if ((index + strlen(options[i].value) + 2) < len)
{
Strncpy(string + index, options[i].value, len - index);
index += strlen(options[i].value);
Strncpy(string + index, ", ", len - index);
index += 2;
}
}
}
if (index > 0)
string[index - 2] = 0;
else
string[0] = 0;
}

View File

@ -0,0 +1,50 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* options.h
*
* $Id: options.h,v 1.7 2001/07/06 23:35:18 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef options_h
#define options_h
#include "tftp_def.h"
/* Structure definition for tftp options. */
struct tftp_opt {
char option[OPT_SIZE];
char value[VAL_SIZE];
int specified; /* specified by the client (for tftp server) */
int enabled; /* enabled for use by server or client */
};
extern struct tftp_opt tftp_default_options[OPT_NUMBER];
int opt_parse_request(char *data, int data_size, struct tftp_opt *options);
int opt_parse_options(char *data, int data_size, struct tftp_opt *options);
int opt_set_options(struct tftp_opt *options, char *name, char *value);
int opt_get_options(struct tftp_opt *options, char *name, char *value);
int opt_disable_options(struct tftp_opt *options, char *name);
int opt_support_options(struct tftp_opt *options);
int opt_get_tsize(struct tftp_opt *options);
int opt_get_timeout(struct tftp_opt *options);
int opt_get_blksize(struct tftp_opt *options);
int opt_get_multicast(struct tftp_opt *options, char *addr, int *port, int *mc);
void opt_set_tsize(int tsize, struct tftp_opt *options);
void opt_set_timeout(int timeout, struct tftp_opt *options);
void opt_set_blksize(int blksize, struct tftp_opt *options);
void opt_set_multicast(struct tftp_opt *options, char *addr, int port, int mc);
void opt_request_to_string(struct tftp_opt *options, char *string, int len);
void opt_options_to_string(struct tftp_opt *options, char *string, int len);
#endif

View File

@ -0,0 +1,180 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* stats.c
* some functions to collect statistics
*
* $Id: stats.c,v 1.6 2002/03/27 03:02:12 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include "config.h"
#include <limits.h>
#include <string.h>
#include "tftp_def.h"
#include "stats.h"
#include "logger.h"
/*
* That structure allows global statistic to be collected. See stats.h.
*/
struct server_stats s_stats;
/*
* Must be called to initilise stats structure and record the
* start time.
*/
void stats_start(void)
{
/* stats struct initialisation */
memset(&s_stats, 0, sizeof(s_stats));
s_stats.min_time.tv_sec = LONG_MAX;
pthread_mutex_init(&s_stats.mutex, NULL);
gettimeofday(&s_stats.start_time, NULL);
}
/*
* Called when execution is finnished, before calling stats_print.
*/
void stats_end(void)
{
gettimeofday(&s_stats.end_time, NULL);
}
/*
* Called by server threads each time a file is sent succesfully.
* Be aware that a mutex is locked in there.
*/
void stats_send_locked(void)
{
pthread_mutex_lock(&s_stats.mutex);
s_stats.number_of_server++;
s_stats.num_file_send++;
pthread_mutex_unlock(&s_stats.mutex);
}
/*
* Called by verver threads each time a file is received.
*/
void stats_recv_locked(void)
{
pthread_mutex_lock(&s_stats.mutex);
s_stats.number_of_server++;
s_stats.num_file_recv++;
pthread_mutex_unlock(&s_stats.mutex);
}
/*
* Called by server threads each time tftpd_send_file or tftpd_recv_file
* return with error.
*/
void stats_err_locked(void)
{
pthread_mutex_lock(&s_stats.mutex);
s_stats.number_of_err++;
pthread_mutex_unlock(&s_stats.mutex);
}
/*
* Called by server threads when the maximum number of threads is reached.
*/
void stats_abort_locked(void)
{
pthread_mutex_lock(&s_stats.mutex);
s_stats.number_of_abort++;
pthread_mutex_unlock(&s_stats.mutex);
}
/*
* Called by main thread only (in fact in tftpd_receive_request(), but
* before stdin_mutex is released) every time a new thread is created.
* We record the number of thread, the number of simultaeous thread, the
* between threads.
*/
void stats_new_thread(int number_of_thread)
{
struct timeval tmp;
if (number_of_thread > s_stats.max_simul_threads)
s_stats.max_simul_threads = number_of_thread;
/* calculate the arrival time of this thread */
if (s_stats.prev_time.tv_sec != 0)
{
gettimeofday(&s_stats.curr_time, NULL);
timeval_diff(&s_stats.diff_time, &s_stats.curr_time,
&s_stats.prev_time);
if (timeval_diff(&tmp, &s_stats.diff_time,
&s_stats.min_time) < 0)
memcpy(&s_stats.min_time, &s_stats.diff_time,
sizeof(struct timeval));
if (timeval_diff(&tmp, &s_stats.diff_time,
&s_stats.max_time) > 0)
memcpy(&s_stats.max_time, &s_stats.diff_time,
sizeof(struct timeval));
memcpy(&s_stats.prev_time, &s_stats.curr_time,
sizeof(struct timeval));
}
else
gettimeofday(&s_stats.prev_time, NULL);
}
/*
* Called by server threads when the finnished to add CPU ressources
* information.
*/
void stats_thread_usage_locked(void)
{
struct tms tms_tmp;
times(&tms_tmp);
pthread_mutex_lock(&s_stats.mutex);
s_stats.tms_thread.tms_utime += tms_tmp.tms_utime;
s_stats.tms_thread.tms_stime += tms_tmp.tms_stime;
pthread_mutex_unlock(&s_stats.mutex);
}
/*
* Called at the end of the main thread, when no other threads are
* running, to print the final statistics.
*/
void stats_print(void)
{
struct timeval tmp;
timeval_diff(&tmp, &s_stats.end_time, &s_stats.start_time);
times(&s_stats.tms);
s_stats.tms.tms_utime += s_stats.tms_thread.tms_utime;
s_stats.tms.tms_stime += s_stats.tms_thread.tms_stime;
logger(LOG_INFO, " Load measurements:");
logger(LOG_INFO, " User: %8.3fs Sys:%8.3fs",
(double)(s_stats.tms.tms_utime) / CLK_TCK,
(double)(s_stats.tms.tms_stime) / CLK_TCK);
logger(LOG_INFO, " Total:%8.3fs CPU:%8.3f%%",
(double)(tmp.tv_sec + tmp.tv_usec * 1e-6),
(double)(s_stats.tms.tms_utime + s_stats.tms.tms_stime) /
(double)(tmp.tv_sec + tmp.tv_usec * 1e-6));
logger(LOG_INFO, " Time between connections:");
if (s_stats.min_time.tv_sec == LONG_MAX)
logger(LOG_INFO, " Min: ----- Max: -----");
else
logger(LOG_INFO, " Min: %.3fs Max: %.3fs",
(double)(s_stats.min_time.tv_sec + s_stats.min_time.tv_usec * 1e-6),
(double)(s_stats.max_time.tv_sec + s_stats.max_time.tv_usec * 1e-6));
logger(LOG_INFO, " Thread stats:");
logger(LOG_INFO, " simultaneous threads: %d", s_stats.max_simul_threads);
logger(LOG_INFO, " number of servers: %d", s_stats.number_of_server);
logger(LOG_INFO, " number of aborts: %d", s_stats.number_of_abort);
logger(LOG_INFO, " number of errors: %d", s_stats.number_of_err);
logger(LOG_INFO, " number of files sent: %d", s_stats.num_file_send);
logger(LOG_INFO, " number of files received: %d", s_stats.num_file_recv);
}

View File

@ -0,0 +1,62 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* stats.h
*
* $Id: stats.h,v 1.3 2000/12/27 17:02:23 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef stats_h
#define stats_h
#include <pthread.h>
#include <sys/time.h>
#include <sys/times.h>
/* structure to collect some stats */
struct server_stats {
/* updated by main thread */
struct timeval start_time;
struct timeval end_time;
struct tms tms;
/* connection statistics, updated by main thread */
int max_simul_threads; /* maximum number of simultaneous server */
struct timeval min_time; /* time between connection stats */
struct timeval max_time;
struct timeval curr_time; /* temporary usage for calculation */
struct timeval prev_time;
struct timeval diff_time;
/* updated by server thread */
pthread_mutex_t mutex;
struct tms tms_thread;
int number_of_server; /* number of server that return successfully */
int number_of_abort; /* when number max of client is reached */
int number_of_err; /* send or receive that return with error */
int num_file_send;
int num_file_recv;
int byte_send; /* total byte transfered to client (file) */
int byte_recv; /* total byte read from client (file) */
};
/* Functions defined in stats.c */
void stats_start(void);
void stats_end(void);
void stats_send_locked(void);
void stats_recv_locked(void);
void stats_err_locked(void);
void stats_abort_locked(void);
void stats_new_thread(int number_of_thread);
void stats_thread_usage_locked(void);
void stats_print(void);
#endif

View File

@ -0,0 +1,183 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* tftp_def.c
*
* $Id: tftp_def.c,v 1.15 2004/02/13 03:16:09 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "tftp_def.h"
#include "options.h"
#include "logger.h"
/*
* This is the default option structure, that must be used
* for initialisation.
*/
// FIXME: is there a way to use TIMEOUT and SEGSIZE here?
struct tftp_opt tftp_default_options[OPT_NUMBER] = {
{ "filename", "", 0, 1}, /* file to transfer */
{ "mode", "octet", 0, 1}, /* mode for transfer */
{ "tsize", "0", 0, 1 }, /* RFC1350 options. See RFC2347, */
{ "timeout", "5", 0, 1 }, /* 2348, 2349, 2090. */
{ "blksize", "512", 0, 1 }, /* This is the default option */
{ "multicast", "", 0, 1 }, /* structure */
{ "", "", 0, 0}
};
/* Error message defined in RFC1350. */
char *tftp_errmsg[9] = {
"Undefined error code",
"File not found",
"Access violation",
"Disk full or allocation exceeded",
"Illegal TFTP operation",
"Unknown transfer ID",
"File already exists",
"No such user",
"Failure to negotiate RFC1782 options",
};
/*
* Compute the difference of two timeval structs handling wrap around.
* The result is retruned in *res.
* Return value are:
* 1 if t1 > t0
* 0 if t1 = t0
* -1 if t1 < t0
*/
int timeval_diff(struct timeval *res, struct timeval *t1, struct timeval *t0)
{
res->tv_sec = t1->tv_sec - t0->tv_sec;
res->tv_usec = t1->tv_usec - t0->tv_usec;
if (res->tv_sec > 0)
{
if (res->tv_usec >= 0)
{
return 1;
}
else
{
res->tv_sec -= 1;
res->tv_usec += 1000000;
return 1;
}
}
else if (res->tv_sec < 0)
{
if (res->tv_usec > 0)
{
res->tv_sec += 1;
res->tv_usec -= 1000000;
return -1;
}
else if (res->tv_usec <= 0);
{
return -1;
}
}
else
{
if (res->tv_usec > 0)
return 1;
else if (res->tv_usec < 0)
return -1;
else
return 0;
}
}
/*
* Print a string in engineering notation.
*
* IN:
* value: value to print
* string: if NULL, the function print to stdout, else if print
* to the string.
* format: format string for printf.
*/
int print_eng(double value, char *string, int size, char *format)
{
char suffix[] = {'f', 'p', 'n', 'u', 'm', 0, 'k', 'M', 'G', 'T', 'P'};
double tmp;
double div = 1e-15;
int i;
for (i = 0; i < 11; i++)
{
tmp = value / div;
if ((tmp > 1.0) && (tmp < 1000.0))
break;
div *= 1000.0;
}
if (string)
snprintf(string, size, format, tmp, suffix[i]);
else
printf(format, tmp, suffix[i]);
return OK;
}
/*
* This is a strncpy function that take care of string NULL termination
*/
inline char *Strncpy(char *to, const char *from, size_t size)
{
to[size-1] = '\000';
return strncpy(to, from, size - 1);
}
/*
* gethostbyname replacement that is reentrant. This function is copyied
* from the libc manual.
*/
int Gethostbyname(char *addr, struct hostent *host)
{
struct hostent *hp;
char *tmpbuf;
size_t tmpbuflen;
int herr;
tmpbuflen = 1024;
if ((tmpbuf = (char *)malloc(tmpbuflen)) == NULL)
return ERR;
hp = gethostbyname_r(addr, host, tmpbuf, tmpbuflen, &herr);
free(tmpbuf);
/* Check for errors. */
if (hp == NULL)
{
logger(LOG_ERR, "%s: %d: gethostbyname_r: %s",
__FILE__, __LINE__, strerror(herr));
return ERR;
}
if (hp != host)
{
logger(LOG_ERR, "%s: %d: abnormal return value",
__FILE__, __LINE__);
return ERR;
}
return OK;
}

View File

@ -0,0 +1,55 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* tftp_def.h
*
* $Id: tftp_def.h,v 1.17 2004/02/13 03:16:09 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef tftp_def_h
#define tftp_def_h
#include <sys/time.h>
#include <sys/times.h>
#include <netdb.h>
/* standard return value */
#define OK 0
#define ERR -1
#define ABORT -2
#define QUIT -10
#define MAXLEN 256 /* For file names and such */
#define IPADDRLEN 24 /* For IPv4 and IPv6 address string */
#define TIMEOUT 5 /* Client timeout */
#define S_TIMEOUT 5 /* Server timout. */
#define NB_OF_RETRY 5
/* definition to use tftp_options structure */
#define OPT_FILENAME 0
#define OPT_MODE 1
#define OPT_TSIZE 2
#define OPT_TIMEOUT 3
#define OPT_BLKSIZE 4
#define OPT_MULTICAST 5
#define OPT_NUMBER 7
#define OPT_SIZE 12
#define VAL_SIZE MAXLEN
extern char *tftp_errmsg[9];
int timeval_diff(struct timeval *res, struct timeval *t1, struct timeval *t0);
int print_eng(double value, char *string, int size, char *format);
inline char *Strncpy(char *to, const char *from, size_t size);
int Gethostbyname(char *addr, struct hostent *host);
#endif

View File

@ -0,0 +1,492 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* tftp_io.c
* I/O operation routines common to both client and server
*
* $Id: tftp_io.c,v 1.24 2004/02/19 01:30:00 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/tftp.h>
#include <errno.h>
#include "string.h"
#include "tftp_io.h"
#include "logger.h"
/*
* 2 bytes string 1 byte string 1 byte string 1 byte string
* --------------------------------------------------------------------->
*| Opcode | Filename | 0 | Mode | 0 | Opt1 | 0 | Value1 <
* --------------------------------------------------------------------->
*
* string 1 byte string 1 byte
* >--------------------------------
* < OptN | 0 | ValueN | 0 |
* >--------------------------------
*/
int tftp_send_request(int socket, struct sockaddr_in *sa, short type,
char *data_buffer, int data_buffer_size,
struct tftp_opt *tftp_options)
{
int i;
int result;
int buf_index = 0;
struct tftphdr *tftphdr = (struct tftphdr *)data_buffer;
char *filename = tftp_options[OPT_FILENAME].value;
char *mode = tftp_options[OPT_MODE].value;
/* write the opcode */
tftphdr->th_opcode = htons(type);
buf_index += 2;
/* write file name */
Strncpy(data_buffer + buf_index, filename, data_buffer_size - buf_index);
buf_index += strlen(filename);
buf_index++;
Strncpy(data_buffer + buf_index, mode, data_buffer_size - buf_index);
buf_index += strlen(mode);
buf_index++;
for (i = 2; ; i++)
{
if (strlen(tftp_options[i].option) == 0)
break;
if (tftp_options[i].enabled && tftp_options[i].specified)
{
Strncpy(data_buffer + buf_index, tftp_options[i].option,
data_buffer_size - buf_index);
buf_index += strlen(tftp_options[i].option);
buf_index++;
Strncpy(data_buffer + buf_index, tftp_options[i].value,
data_buffer_size - buf_index);
buf_index += strlen(tftp_options[i].value);
buf_index++;
}
}
/* send the buffer */
result = sendto(socket, data_buffer, buf_index, 0,
(struct sockaddr *)sa, sizeof(*sa));
if (result < 0)
return ERR;
return OK;
}
/*
* 2 bytes 2 bytes
* -------------------
*| Opcode | Block # |
* -------------------
*/
int tftp_send_ack(int socket, struct sockaddr_in *sa, short block_number)
{
struct tftphdr tftphdr;
int result;
tftphdr.th_opcode = htons(ACK);
tftphdr.th_block = htons(block_number);
result = sendto(socket, &tftphdr, 4, 0, (struct sockaddr *)sa,
sizeof(*sa));
if (result < 0)
return ERR;
return OK;
}
/*
* 2 bytes string 1 byte string 1 byte string 1 byte string 1 byte
* ---------------------------------------------------------------------------
*| Opcode | Opt1 | 0 | Value1 | 0 | OptN | 0 | ValueN | 0 |
* ---------------------------------------------------------------------------
*/
int tftp_send_oack(int socket, struct sockaddr_in *sa, struct tftp_opt *tftp_options,
char *buffer, int buffer_size)
{
int i;
int result;
int index = 0;
struct tftphdr *tftphdr = (struct tftphdr *)buffer;
/* write the opcode */
tftphdr->th_opcode = htons(OACK);
index += 2;
for (i = 2; i < OPT_NUMBER; i++)
{
if (tftp_options[i].enabled && tftp_options[i].specified)
{
Strncpy(buffer + index, tftp_options[i].option, buffer_size - index);
index += strlen(tftp_options[i].option);
index++;
Strncpy(buffer + index, tftp_options[i].value, buffer_size - index);
index += strlen(tftp_options[i].value);
index++;
}
}
/* send the buffer */
result = sendto(socket, buffer, index, 0, (struct sockaddr *)sa,
sizeof(*sa));
if (result < 0)
return ERR;
return OK;
}
/*
* 2 bytes 2 bytes string 1 byte
* ---------------------------------------
*| Opcode | ErrorCode | ErrMsg | 0 |
* ---------------------------------------
*/
int tftp_send_error(int socket, struct sockaddr_in *sa, short err_code,
char *buffer, int buffer_size)
{
int size;
int result;
struct tftphdr *tftphdr = (struct tftphdr *)buffer;
if (err_code > EOPTNEG)
return ERR;
tftphdr->th_opcode = htons(ERROR);
tftphdr->th_code = htons(err_code);
Strncpy(tftphdr->th_msg, tftp_errmsg[err_code], buffer_size - 4);
size = 4 + strlen(tftp_errmsg[err_code]) + 1;
result = sendto(socket, tftphdr, size, 0, (struct sockaddr *)sa,
sizeof(*sa));
if (result < 0)
return ERR;
return OK;
}
/*
* 2 bytes 2 bytes N bytes
* ----------------------------
*| Opcode | Block # | Data |
* ----------------------------
*/
int tftp_send_data(int socket, struct sockaddr_in *sa, short block_number,
int size, char *data)
{
struct tftphdr *tftphdr = (struct tftphdr *)data;
int result;
tftphdr->th_opcode = htons(DATA);
tftphdr->th_block = htons(block_number);
result = sendto(socket, data, size, 0, (struct sockaddr *)sa,
sizeof(*sa));
if (result < 0)
return ERR;
return OK;
}
/*
* Wait for a packet. This function can listen on 2 sockets. This is
* needed by the multicast tftp client.
*/
int tftp_get_packet(int sock1, int sock2, int *sock, struct sockaddr_in *sa,
struct sockaddr_in *sa_from, struct sockaddr_in *sa_to,
int timeout, int *size, char *data)
{
int result;
struct timeval tv;
fd_set rfds;
struct sockaddr_in from;
struct tftphdr *tftphdr = (struct tftphdr *)data;
struct msghdr msg; /* used to get client's packet info */
struct cmsghdr *cmsg;
struct iovec iov;
char cbuf[1024];
/* initialise structure */
memset(&from, 0, sizeof(from));
iov.iov_base = data;
iov.iov_len = *size;
msg.msg_name = &from;
msg.msg_namelen = sizeof(from);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cbuf;
msg.msg_controllen = sizeof(cbuf);
/* Wait up to five seconds. */
tv.tv_sec = timeout;
tv.tv_usec = 0;
/* Watch socket to see when it has input. */
FD_ZERO(&rfds);
FD_SET(sock1, &rfds);
if (sock2 > -1)
FD_SET(sock2, &rfds);
/* wait for data on sockets */
result = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
switch (result)
{
case -1:
logger(LOG_ERR, "select: %s", strerror(errno));
return ERR;
case 0:
return GET_TIMEOUT;
break;
case 1:
case 2:
result = 0;
if (FD_ISSET(sock1, &rfds))
{
result = recvmsg(sock1, &msg, 0);
if (sock)
*sock = sock1;
}
else
{
if ((sock2 > -1) && (FD_ISSET(sock2, &rfds)))
{
result = recvmsg(sock2, &msg, 0);
if (sock)
*sock = sock2;
}
}
if (result == 0)
return ERR;
if (result == -1)
{
logger(LOG_ERR, "recvmsg: %s", strerror(errno));
return ERR;
}
/* if needed read data from message control */
if (sa_to)
{
for (cmsg = CMSG_FIRSTHDR(&msg);
cmsg != NULL && cmsg->cmsg_len >= sizeof(*cmsg);
cmsg = CMSG_NXTHDR(&msg, cmsg))
{
#ifndef __HAIKU__
if (cmsg->cmsg_level == SOL_IP
&& cmsg->cmsg_type == IP_PKTINFO)
{
struct in_pktinfo *pktinfo;
pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
sa_to->sin_addr = pktinfo->ipi_addr;
}
#else // !__HAIKU__
if (cmsg->cmsg_level == IPPROTO_IP
&& cmsg->cmsg_type == IP_RECVDSTADDR)
{
struct in_addr *addr = (struct in_addr *)CMSG_DATA(cmsg);
sa_to->sin_addr = *addr;
}
#endif
break;
}
}
/* return the size to the caller */
*size = result;
/* return the peer address/port to the caller */
if (sa_from != NULL)
memcpy(sa_from, &from, sizeof(from));
/* if sa as never been initialised, sa->sin_port is still 0 */
if (sa->sin_port == htons(0))
memcpy(sa, &from, sizeof(from));
switch (ntohs(tftphdr->th_opcode))
{
case RRQ:
return GET_RRQ;
case WRQ:
return GET_WRQ;
case ACK:
return GET_ACK;
case OACK:
return GET_OACK;
case ERROR:
return GET_ERROR;
case DATA:
return GET_DATA;
default:
return GET_DISCARD;
}
break;
default:
return ERR;
}
}
/*
* Read from file and do netascii conversion if needed
*/
int tftp_file_read(FILE *fp, char *data_buffer, int data_buffer_size, int block_number,
int convert, int *prev_block_number, int *prev_file_pos, int *temp)
{
int i;
int c;
char prevchar = *temp & 0xff;
char newline = (*temp & 0xff00) >> 8;
int data_size;
if (!convert)
{
/* In this case, just read the requested data block.
Anyway, in the multicast case it can be in random
order. */
fseek(fp, block_number * data_buffer_size, SEEK_SET);
data_size = fread(data_buffer, 1, data_buffer_size, fp);
return data_size;
}
else
{
/*
* When converting data, it become impossible to seek in
* the file based on the block number. So we must always
* remeber the position in the file from were to read the
* data requested by the client. Client can only request data
* for the same block or the next, but we cannot assume this
* block number will increase at every ACK since it can be
* lost in transmission.
*
* The stategy is to remeber the file position as well as
* the block number from the current call to this function.
* If the client request a block number different from that
* we return ERR.
*
* If the client request many time the same block, the
* netascii conversion is done each time. Since this is not
* a normal condition it should not be a problem for system
* performance.
*
*/
if ((block_number != *prev_block_number) && (block_number != *prev_block_number + 1))
return ERR;
if (block_number == *prev_block_number)
fseek(fp, *prev_file_pos, SEEK_SET);
*prev_block_number = block_number;
*prev_file_pos = ftell(fp);
/*
* convert to netascii, based on netkit-tftp-0.17 routine in tftpsubs.c
* i index output buffer
*/
for (i = 0; i < data_buffer_size; i++)
{
if (newline)
{
if (prevchar == '\n')
c = '\n'; /* lf to cr,lf */
else
c = '\0'; /* cr to cr,nul */
newline = 0;
}
else
{
c = fgetc(fp);
if (c == EOF)
break;
if (c == '\n' || c == '\r')
{
prevchar = c;
c = '\r';
newline = 1;
}
}
data_buffer[i] = c;
}
/* save state */
*temp = (newline << 8) | prevchar;
return i;
}
}
/*
* Write to file and do netascii conversion if needed
*/
int tftp_file_write(FILE *fp, char *data_buffer, int data_buffer_size, int block_number, int data_size,
int convert, int *prev_block_number, int *temp)
{
int i;
int c;
char prevchar = *temp;
if (!convert)
{
/* Simple case, just seek and write */
fseek(fp, (block_number - 1) * data_buffer_size, SEEK_SET);
data_size = fwrite(data_buffer, 1, data_size, fp);
return data_size;
}
else
{
/*
* Same principle than for reading, but simpler since when client
* send same block twice there is no need to rewrite it to the
* file
*/
if ((block_number != *prev_block_number) && (block_number != *prev_block_number + 1))
return ERR;
if (block_number == *prev_block_number)
return data_size;
*prev_block_number = block_number;
/*
* convert to netascii, based on netkit-tftp-0.17 routine in tftpsubs.c
* i index input buffer
*/
for (i = 0; i < data_size; i++)
{
c = data_buffer[i];
if (prevchar == '\r')
{
if (c == '\n')
{
fseek(fp, -1, SEEK_CUR); /* cr,lf to lf */
if (fputc(c, fp) == EOF)
break;
}
else if (c != '\0') /* cr,nul to cr */
{
if (fputc(c, fp) == EOF)
break;
}
}
else
{
if (fputc(c, fp) == EOF)
break;
}
prevchar = c;
}
/* save state */
*temp = prevchar;
return i;
}
}

View File

@ -0,0 +1,59 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* tftp_io.h
*
* $Id: tftp_io.h,v 1.18 2004/02/13 03:16:09 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef tftp_io_h
#define tftp_io_h
#include <arpa/tftp.h>
#include <sys/socket.h>
#include "tftp_def.h"
#include "options.h"
/* missing from <arpa/tftp.h> */
/* new opcode */
#define OACK 06
/* new error code */
#define EOPTNEG 8 /* error in option negociation */
/* return value of tftp_get_packet */
#define GET_DISCARD 0
#define GET_TIMEOUT 1
#define GET_RRQ 2
#define GET_WRQ 3
#define GET_ACK 4
#define GET_OACK 5
#define GET_ERROR 6
#define GET_DATA 7
/* functions prototype */
int tftp_send_request(int socket, struct sockaddr_in *s_inn, short type,
char *data_buffer, int data_buffer_size,
struct tftp_opt *tftp_options);
int tftp_send_ack(int socket, struct sockaddr_in *s_inn, short block_number);
int tftp_send_oack(int socket, struct sockaddr_in *s_inn, struct tftp_opt *tftp_options,
char *buffer, int buffer_size);
int tftp_send_error(int socket, struct sockaddr_in *s_inn, short err_code,
char *buffer, int buffer_size);
int tftp_send_data(int socket, struct sockaddr_in *s_inn, short block_number,
int size, char *data);
int tftp_get_packet(int sock1, int sock2, int *sock, struct sockaddr_in *sa,
struct sockaddr_in *from, struct sockaddr_in *to,
int timeout, int *size, char *data);
int tftp_file_read(FILE *fp, char *buffer, int buffer_size, int block_number, int convert,
int *prev_block_number, int *prev_file_pos, int *temp);
int tftp_file_write(FILE *fp, char *data_buffer, int data_buffer_size, int block_number,
int data_size, int convert, int *prev_block_number, int *temp);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,114 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* tftpd.h
*
* $Id: tftpd.h,v 1.22 2004/02/27 02:05:26 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef tftpd_h
#define tftpd_h
#include <pthread.h>
#include <arpa/tftp.h>
#include <arpa/inet.h>
#include "tftp_io.h"
/*
* Per thread data. There is a thread for each client or group
* (multicast) of client.
*/
struct thread_data {
pthread_t tid;
/* private to thread */
char *data_buffer;
int data_buffer_size;
int timeout;
int checkport; /* Disable TID check. Violate RFC */
int mcast_switch_client; /* When set, server will switch to next client
on first timeout from the current client. */
int trace;
int sockfd;
/* multicast stuff */
short mc_port; /* multicast port */
char *mc_addr; /* multicast address */
struct sockaddr_in sa_mcast;
struct ip_mreq mcastaddr;
u_char mcast_ttl;
/*
* Self can read/write until client_ready is set. Then only allowed to read.
* Other thread can read it only when client_ready is set. Remember that access
* to client_ready bellow is protected by a mutex.
*/
struct tftp_opt *tftp_options;
/*
* Must lock to insert in the list or search, but not to read or write
* in the client_info structure, since only the propriotary thread do it.
*/
pthread_mutex_t client_mutex;
struct client_info *client_info;
int client_ready; /* one if other thread may add client */
/* must be lock (list lock) to update */
struct thread_data *prev;
struct thread_data *next;
};
struct client_info {
struct sockaddr_in client;
int done; /* that client as receive it's file */
struct client_info *next;
};
/*
* Functions defined in tftpd_file.c
*/
int tftpd_rules_check(char *filename);
int tftpd_receive_file(struct thread_data *data);
int tftpd_send_file(struct thread_data *data);
/*
* Defined in tftpd_list.c, operation on thread_data list.
*/
int tftpd_list_add(struct thread_data *new);
int tftpd_list_remove(struct thread_data *old);
int tftpd_list_num_of_thread(void);
int tftpd_list_find_multicast_server_and_add(struct thread_data **thread,
struct thread_data *data,
struct client_info *client);
/*
* Defined in tftpd_list.c, operation on client structure list.
*/
inline void tftpd_clientlist_ready(struct thread_data *thread);
void tftpd_clientlist_remove(struct thread_data *thread,
struct client_info *client);
void tftpd_clientlist_free(struct thread_data *thread);
int tftpd_clientlist_done(struct thread_data *thread,
struct client_info *client,
struct sockaddr_in *sock);
int tftpd_clientlist_next(struct thread_data *thread,
struct client_info **client);
void tftpd_list_kill_threads(void);
/*
* Defines in tftpd_mcast.c
*/
int tftpd_mcast_get_tid(char **addr, short *port);
int tftpd_mcast_free_tid(char *addr, short port);
int tftpd_mcast_parse_opt(char *addr, char *ports);
#endif

View File

@ -0,0 +1,980 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* tftpd_file.c
* state machine for file transfer on the server side
*
* $Id: tftpd_file.c,v 1.51 2004/02/18 02:21:47 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include "tftpd.h"
#include "tftp_io.h"
#include "tftp_def.h"
#include "logger.h"
#include "options.h"
#ifdef HAVE_PCRE
#include "tftpd_pcre.h"
#endif
#define S_BEGIN 0
#define S_SEND_REQ 1
#define S_SEND_ACK 2
#define S_SEND_OACK 3
#define S_SEND_DATA 4
#define S_WAIT_PACKET 5
#define S_REQ_RECEIVED 6
#define S_ACK_RECEIVED 7
#define S_OACK_RECEIVED 8
#define S_DATA_RECEIVED 9
#define S_ABORT 10
#define S_END 11
/* read only variables unless for the main thread, at initialisation */
extern char directory[MAXLEN];
/* read only except for the main thread */
extern int tftpd_cancel;
#ifdef HAVE_PCRE
extern tftpd_pcre_self_t *pcre_top;
#endif
/*
* Rules for filenames. This is common to both tftpd_recieve_file
* and tftpd_send_file It is placed here to make sure the rules are
* consistent and clearer.
*/
int tftpd_rules_check(char *filename)
{
char string[MAXLEN];
/* If the filename does not start with the directory, change it */
if (strncmp(directory, filename, strlen(directory)) != 0)
{
/* Append the prefix directory to the requested file. */
snprintf(string, sizeof(string), "%s%s",
directory, filename);
Strncpy(filename, string, MAXLEN);
}
/* If the filename contain /../ sequences, we forbid the access */
if (strstr(filename, "/../") != NULL)
{
/* Illegal access. */
logger(LOG_INFO, "File name with /../ are forbidden");
return ERR;
}
return OK;
}
/*
* Receive a file. It is implemented as a state machine using a while loop
* and a switch statement. Function flow is as follow:
* - sanity check
* - check client's request
* - enter state machine
*
* 1) send a ACK or OACK
* 2) wait replay
* - if DATA packet, read it, send an acknoledge, goto 2
* - if ERROR abort
* - if TIMEOUT goto previous state
*/
int tftpd_receive_file(struct thread_data *data)
{
int state = S_BEGIN;
int timeout_state = state;
int result;
int block_number = 0;
int data_size;
int sockfd = data->sockfd;
struct sockaddr_in *sa = &data->client_info->client;
struct sockaddr_in from;
struct tftphdr *tftphdr = (struct tftphdr *)data->data_buffer;
FILE *fp;
char filename[MAXLEN];
char string[MAXLEN];
int timeout = data->timeout;
int number_of_timeout = 0;
int all_blocks_received = 0; /* temporary kludge */
int convert = 0; /* if true, do netascii convertion */
int prev_block_number = 0; /* needed to support netascii convertion */
int temp = 0;
/* look for mode option */
if (strcasecmp(data->tftp_options[OPT_MODE].value, "netascii") == 0)
{
convert = 1;
logger(LOG_DEBUG, "will do netascii convertion");
}
/* file name verification */
Strncpy(filename, data->tftp_options[OPT_FILENAME].value,
MAXLEN);
if (tftpd_rules_check(filename) != OK)
{
tftp_send_error(sockfd, sa, EACCESS, data->data_buffer, data->data_buffer_size);
if (data->trace)
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EACCESS,
tftp_errmsg[EACCESS]);
return ERR;
}
/* Open the file for writing. */
if ((fp = fopen(filename, "w")) == NULL)
{
/* Can't create the file. */
logger(LOG_INFO, "Can't open %s for writing", filename);
tftp_send_error(sockfd, sa, EACCESS, data->data_buffer, data->data_buffer_size);
if (data->trace)
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EACCESS,
tftp_errmsg[EACCESS]);
return ERR;
}
/* tsize option */
if (((result = opt_get_tsize(data->tftp_options)) > -1) && !convert)
{
opt_set_tsize(result, data->tftp_options);
logger(LOG_DEBUG, "tsize option -> %d", result);
}
/* timeout option */
if ((result = opt_get_timeout(data->tftp_options)) > -1)
{
if ((result < 1) || (result > 255))
{
tftp_send_error(sockfd, sa, EOPTNEG, data->data_buffer, data->data_buffer_size);
if (data->trace)
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EOPTNEG,
tftp_errmsg[EOPTNEG]);
fclose(fp);
return ERR;
}
timeout = result;
opt_set_timeout(timeout, data->tftp_options);
logger(LOG_DEBUG, "timeout option -> %d", timeout);
}
/* blksize options */
if ((result = opt_get_blksize(data->tftp_options)) > -1)
{
if ((result < 8) || (result > 65464))
{
tftp_send_error(sockfd, sa, EOPTNEG, data->data_buffer, data->data_buffer_size);
if (data->trace)
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EOPTNEG,
tftp_errmsg[EOPTNEG]);
fclose(fp);
return ERR;
}
data->data_buffer_size = result + 4;
data->data_buffer = realloc(data->data_buffer, data->data_buffer_size);
if (data->data_buffer == NULL)
{
logger(LOG_ERR, "memory allocation failure");
fclose(fp);
return ERR;
}
tftphdr = (struct tftphdr *)data->data_buffer;
if (data->data_buffer == NULL)
{
tftp_send_error(sockfd, sa, ENOSPACE, data->data_buffer, data->data_buffer_size);
if (data->trace)
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", ENOSPACE,
tftp_errmsg[ENOSPACE]);
fclose(fp);
return ERR;
}
opt_set_blksize(result, data->tftp_options);
logger(LOG_DEBUG, "blksize option -> %d", result);
}
/* that's it, we start receiving the file */
while (1)
{
if (tftpd_cancel)
{
logger(LOG_DEBUG, "thread cancelled");
tftp_send_error(sockfd, sa, EUNDEF, data->data_buffer, data->data_buffer_size);
if (data->trace)
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EUNDEF,
tftp_errmsg[EUNDEF]);
state = S_ABORT;
}
switch (state)
{
case S_BEGIN:
/* Did the client request RFC1350 options ?*/
if (opt_support_options(data->tftp_options))
state = S_SEND_OACK;
else
state = S_SEND_ACK;
break;
case S_SEND_ACK:
timeout_state = state;
tftp_send_ack(sockfd, sa, block_number);
if (data->trace)
logger(LOG_DEBUG, "sent ACK <block: %d>", block_number);
if (all_blocks_received)
state = S_END;
else
state = S_WAIT_PACKET;
break;
case S_SEND_OACK:
timeout_state = state;
tftp_send_oack(sockfd, sa, data->tftp_options,
data->data_buffer, data->data_buffer_size);
opt_options_to_string(data->tftp_options, string, MAXLEN);
if (data->trace)
logger(LOG_DEBUG, "sent OACK <%s>", string);
state = S_WAIT_PACKET;
break;
case S_WAIT_PACKET:
data_size = data->data_buffer_size;
result = tftp_get_packet(sockfd, -1, NULL, sa, &from, NULL,
timeout, &data_size, data->data_buffer);
switch (result)
{
case GET_TIMEOUT:
number_of_timeout++;
if (number_of_timeout > NB_OF_RETRY)
{
logger(LOG_INFO, "client (%s) not responding",
inet_ntoa(data->client_info->client.sin_addr));
state = S_END;
}
else
{
logger(LOG_WARNING, "timeout: retrying...");
state = timeout_state;
}
break;
case GET_ERROR:
/*
* This does not work correctly if load balancers are
* employed on one side of a multihomed host. ie, the
* tftp RRQ goes through the load balancer and has the
* source ports changed while the multicast traffic
* bypasses the load balancers.
*
* **** It is not compliant with RFC1350 to skip this
* **** test since the port number is the TID. Use this
* **** only if you know what you're doing.
*/
if (sa->sin_port != from.sin_port)
{
if (data->checkport)
{
logger(LOG_WARNING, "packet discarded <%s>",
inet_ntoa(from.sin_addr));
break;
}
else
logger(LOG_WARNING, "source port mismatch, check bypassed");
}
Strncpy(string, tftphdr->th_msg,
(((data_size - 4) > MAXLEN) ? MAXLEN :
(data_size - 4)));
if (data->trace)
logger(LOG_DEBUG, "received ERROR <code: %d, msg: %s>",
ntohs(tftphdr->th_code), string);
state = S_ABORT;
break;
case GET_DATA:
/* Check that source port match */
if (sa->sin_port != from.sin_port)
{
if (data->checkport)
{
logger(LOG_WARNING, "packet discarded <%s>",
inet_ntoa(from.sin_addr));
break;
}
else
logger(LOG_WARNING, "source port mismatch, check bypassed");
}
number_of_timeout = 0;
state = S_DATA_RECEIVED;
break;
case GET_DISCARD:
/* FIXME: should we increment number_of_timeout */
logger(LOG_WARNING, "packet discarded <%s>",
inet_ntoa(from.sin_addr));
break;
case ERR:
logger(LOG_ERR, "%s: %d: recvfrom: %s",
__FILE__, __LINE__, strerror(errno));
state = S_ABORT;
break;
default:
logger(LOG_ERR, "%s: %d: abnormal return value %d",
__FILE__, __LINE__, result);
state = S_ABORT;
}
break;
case S_DATA_RECEIVED:
/* We need to seek to the right place in the file */
block_number = ntohs(tftphdr->th_block);
if (data->trace)
logger(LOG_DEBUG, "received DATA <block: %d, size: %d>",
block_number, data_size - 4);
if (tftp_file_write(fp, tftphdr->th_data, data->data_buffer_size - 4, block_number,
data_size - 4, convert, &prev_block_number, &temp)
!= data_size - 4)
{
logger(LOG_ERR, "%s: %d: error writing to file %s",
__FILE__, __LINE__, filename);
tftp_send_error(sockfd, sa, ENOSPACE, data->data_buffer,
data->data_buffer_size);
if (data->trace)
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>",
ENOSPACE, tftp_errmsg[ENOSPACE]);
state = S_ABORT;
break;
}
if (data_size < data->data_buffer_size)
all_blocks_received = 1;
else
all_blocks_received = 0;
state = S_SEND_ACK;
break;
case S_END:
fclose(fp);
return OK;
case S_ABORT:
fclose(fp);
return ERR;
default:
fclose(fp);
logger(LOG_ERR, "%s: %d: tftpd_file.c: huh?",
__FILE__, __LINE__);
return ERR;
}
}
}
/*
* Send a file. It is implemented as a state machine using a while loop
* and a switch statement. Function flow is as follow:
* - sanity check
* - check client's request
* - enter state machine
*
* 1) send a DATA or OACK
* 2) wait replay
* - if ACK, goto 3
* - if ERROR abort
* - if TIMEOUT goto previous state
* 3) send data, goto 2
*/
int tftpd_send_file(struct thread_data *data)
{
int state = S_BEGIN;
int timeout_state = state;
int result;
int block_number = 0;
int last_block = -1;
int data_size;
struct sockaddr_in *sa = &data->client_info->client;
struct sockaddr_in from;
int sockfd = data->sockfd;
struct tftphdr *tftphdr = (struct tftphdr *)data->data_buffer;
FILE *fp;
char filename[MAXLEN];
char string[MAXLEN];
int timeout = data->timeout;
int number_of_timeout = 0;
int mcast_switch = data->mcast_switch_client;
struct stat file_stat;
int convert = 0; /* if true, do netascii conversion */
struct thread_data *thread = NULL; /* used when looking for a multicast
thread */
int multicast = 0; /* set to 1 if multicast */
struct client_info *client_info = data->client_info;
struct client_info *client_old = NULL;
struct tftp_opt options[OPT_NUMBER];
int prev_block_number = 0; /* needed to support netascii convertion */
int prev_file_pos = 0;
int temp = 0;
/* look for mode option */
if (strcasecmp(data->tftp_options[OPT_MODE].value, "netascii") == 0)
{
convert = 1;
logger(LOG_DEBUG, "will do netascii convertion");
}
/* file name verification */
Strncpy(filename, data->tftp_options[OPT_FILENAME].value,
MAXLEN);
if (tftpd_rules_check(filename) != OK)
{
tftp_send_error(sockfd, sa, EACCESS, data->data_buffer, data->data_buffer_size);
if (data->trace)
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EACCESS,
tftp_errmsg[EACCESS]);
return ERR;
}
/* verify that the requested file exist */
fp = fopen(filename, "r");
#ifdef HAVE_PCRE
if (fp == NULL)
{
/* Verify if this file have a working subsitution */
if (pcre_top != NULL)
{
if (tftpd_pcre_sub(pcre_top, string, MAXLEN,
data->tftp_options[OPT_FILENAME].value) < 0)
{
logger(LOG_DEBUG, "PCRE failed to match");
}
else
{
logger(LOG_INFO, "PCRE mapped %s -> %s",
data->tftp_options[OPT_FILENAME].value, string);
Strncpy(filename, string, MAXLEN);
/* recheck those rules */
if (tftpd_rules_check(filename) != OK)
{
tftp_send_error(sockfd, sa, EACCESS, data->data_buffer,
data->data_buffer_size);
if (data->trace)
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EACCESS,
tftp_errmsg[EACCESS]);
return ERR;
}
/* write back the new file name to the option structure */
opt_set_options(data->tftp_options, "filename", filename);
/* try to open this new file */
fp = fopen(filename, "r");
}
}
}
#endif
if (fp == NULL)
{
tftp_send_error(sockfd, sa, ENOTFOUND, data->data_buffer, data->data_buffer_size);
logger(LOG_INFO, "File %s not found", filename);
if (data->trace)
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", ENOTFOUND,
tftp_errmsg[ENOTFOUND]);
return ERR;
}
/* To return the size of the file with tsize argument */
fstat(fileno(fp), &file_stat);
/* tsize option */
if ((opt_get_tsize(data->tftp_options) > -1) && !convert)
{
opt_set_tsize(file_stat.st_size, data->tftp_options);
logger(LOG_INFO, "tsize option -> %d", file_stat.st_size);
}
/* timeout option */
if ((result = opt_get_timeout(data->tftp_options)) > -1)
{
if ((result < 1) || (result > 255))
{
tftp_send_error(sockfd, sa, EOPTNEG, data->data_buffer, data->data_buffer_size);
if (data->trace)
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EOPTNEG,
tftp_errmsg[EOPTNEG]);
fclose(fp);
return ERR;
}
timeout = result;
opt_set_timeout(timeout, data->tftp_options);
logger(LOG_INFO, "timeout option -> %d", timeout);
}
/* blksize options */
if ((result = opt_get_blksize(data->tftp_options)) > -1)
{
if ((result < 8) || (result > 65464))
{
tftp_send_error(sockfd, sa, EOPTNEG, data->data_buffer, data->data_buffer_size);
if (data->trace)
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EOPTNEG,
tftp_errmsg[EOPTNEG]);
fclose(fp);
return ERR;
}
data->data_buffer_size = result + 4;
data->data_buffer = realloc(data->data_buffer, data->data_buffer_size);
if (data->data_buffer == NULL)
{
logger(LOG_ERR, "memory allocation failure");
fclose(fp);
return ERR;
}
tftphdr = (struct tftphdr *)data->data_buffer;
if (data->data_buffer == NULL)
{
tftp_send_error(sockfd, sa, ENOSPACE, data->data_buffer, data->data_buffer_size);
if (data->trace)
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", ENOSPACE,
tftp_errmsg[ENOSPACE]);
fclose(fp);
return ERR;
}
opt_set_blksize(result, data->tftp_options);
logger(LOG_INFO, "blksize option -> %d", result);
}
/* Verify that the file can be sent in 2^16 block of BLKSIZE octets */
if ((file_stat.st_size / (data->data_buffer_size - 4)) > 65535)
{
tftp_send_error(sockfd, sa, EUNDEF, data->data_buffer, data->data_buffer_size);
logger(LOG_NOTICE, "Requested file to big, increase BLKSIZE");
if (data->trace)
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EUNDEF,
tftp_errmsg[EUNDEF]);
fclose(fp);
return ERR;
}
/* multicast option */
if (data->tftp_options[OPT_MULTICAST].specified &&
data->tftp_options[OPT_MULTICAST].enabled && !convert)
{
/*
* Find a server with the same options to give up the client.
*/
logger(LOG_DEBUG, "Searching a server thread to give up this client");
result = tftpd_list_find_multicast_server_and_add(&thread, data, data->client_info);
if ( result > 0)
{
/* add this client to its list of client */
if (result == 1)
logger(LOG_DEBUG, "Added client %p to thread %p", data->client_info,
thread);
else
logger(LOG_DEBUG, "Client (%p) is already in list of thread %p",
data->client_info, thread);
/* NULL our own pointer so we don't free memory */
if (result == 1)
data->client_info = NULL;
/* Look at needed information to oack that client */
opt_set_multicast(data->tftp_options, thread->mc_addr,
thread->mc_port, 0);
logger(LOG_INFO, "multicast option -> %s,%d,%d",
thread->mc_addr, thread->mc_port, 0);
/* Send an OACK to that client. There is a possible race condition
here where the new server thread OACK this client before us. This should
not be a problem: the client thread will receive a second OACK and fall
back to non master mode. Then the server will timeout and either resend
OACK or continu with the next client */
opt_options_to_string(data->tftp_options, string, MAXLEN);
if (data->trace)
logger(LOG_DEBUG, "sent OACK <%s>", string);
tftp_send_oack(thread->sockfd, sa, data->tftp_options,
data->data_buffer, data->data_buffer_size);
/* We are done */
logger(LOG_INFO, "Client transfered to %p", thread);
fclose(fp);
return OK;
}
else
{
/* configure socket, get an IP address */
if (tftpd_mcast_get_tid(&data->mc_addr, &data->mc_port) != OK)
{
logger(LOG_ERR, "No multicast address/port available");
fclose(fp);
return ERR;
}
logger(LOG_DEBUG, "mcast_addr: %s, mcast_port: %d",
data->mc_addr, data->mc_port);
/* convert address */
if (inet_aton(data->mc_addr, &data->sa_mcast.sin_addr) == 0)
{
logger(LOG_ERR, "bad address %s\n",data->mc_addr);
fclose(fp);
return ERR;
}
data->sa_mcast.sin_family = AF_INET; /* FIXME: IPv6 */
data->sa_mcast.sin_port = htons(data->mc_port);
/* verify address is multicast */
if (!IN_MULTICAST(ntohl(data->sa_mcast.sin_addr.s_addr)))
{
logger(LOG_ERR, "bad multicast address %s\n",
inet_ntoa(data->sa_mcast.sin_addr));
fclose(fp);
return ERR;
}
/* initialise multicast address structure */
data->mcastaddr.imr_multiaddr.s_addr =
data->sa_mcast.sin_addr.s_addr;
data->mcastaddr.imr_interface.s_addr = htonl(INADDR_ANY);
setsockopt(data->sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
&data->mcast_ttl, sizeof(data->mcast_ttl));
/* set options data for OACK */
opt_set_multicast(data->tftp_options, data->mc_addr,
data->mc_port, 1);
logger(LOG_INFO, "multicast option -> %s,%d,%d", data->mc_addr,
data->mc_port, 1);
/* the socket must be unconnected for multicast */
sa->sin_family = AF_UNSPEC;
connect(sockfd, (struct sockaddr *)sa, sizeof(sa));
/* set multicast flag */
multicast = 1;
/* Now ready to receive new clients */
tftpd_clientlist_ready(data);
}
}
/* copy options to local structure, used when falling back a client to slave */
memcpy(options, data->tftp_options, sizeof(options));
opt_set_multicast(options, data->mc_addr, data->mc_port, 0);
/* That's it, ready to send the file */
while (1)
{
if (tftpd_cancel)
{
/* Send error to all client */
logger(LOG_DEBUG, "thread cancelled");
do
{
tftpd_clientlist_done(data, client_info, NULL);
tftp_send_error(sockfd, &client_info->client,
EUNDEF, data->data_buffer, data->data_buffer_size);
if (data->trace)
{
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s> to %s", EUNDEF,
tftp_errmsg[EUNDEF], inet_ntoa(client_info->client.sin_addr));
}
} while (tftpd_clientlist_next(data, &client_info) == 1);
state = S_ABORT;
}
switch (state)
{
case S_BEGIN:
if (opt_support_options(data->tftp_options))
state = S_SEND_OACK;
else
state = S_SEND_DATA;
break;
case S_SEND_OACK:
timeout_state = state;
opt_options_to_string(data->tftp_options, string, MAXLEN);
if (data->trace)
logger(LOG_DEBUG, "sent OACK <%s>", string);
tftp_send_oack(sockfd, sa, data->tftp_options,
data->data_buffer, data->data_buffer_size);
state = S_WAIT_PACKET;
break;
case S_SEND_DATA:
timeout_state = state;
data_size = tftp_file_read(fp, tftphdr->th_data, data->data_buffer_size - 4, block_number,
convert, &prev_block_number, &prev_file_pos, &temp);
data_size += 4; /* need to consider tftp header */
/* record the last block number */
if (feof(fp))
last_block = block_number;
if (multicast)
{
tftp_send_data(sockfd, &data->sa_mcast,
block_number + 1, data_size,
data->data_buffer);
}
else
{
tftp_send_data(sockfd, sa, block_number + 1,
data_size, data->data_buffer);
}
if (data->trace)
logger(LOG_DEBUG, "sent DATA <block: %d, size %d>",
block_number + 1, data_size - 4);
state = S_WAIT_PACKET;
break;
case S_WAIT_PACKET:
data_size = data->data_buffer_size;
result = tftp_get_packet(sockfd, -1, NULL, sa, &from, NULL,
timeout, &data_size, data->data_buffer);
switch (result)
{
case GET_TIMEOUT:
number_of_timeout++;
if (number_of_timeout > NB_OF_RETRY)
{
logger(LOG_INFO, "client (%s) not responding",
inet_ntoa(client_info->client.sin_addr));
state = S_END;
}
else
{
/* The client failed to ACK our last packet. Send an
OACK with mc=0 to it, fetch the next client in the
list and continu with this new one */
if (multicast && mcast_switch)
{
client_old = client_info;
tftpd_clientlist_next(data, &client_info);
if (client_info && (client_info != client_old))
{
/* Send an OACK to the old client remove is
master client status */
opt_options_to_string(options,
string, MAXLEN);
if (data->trace)
logger(LOG_DEBUG, "sent OACK <%s>", string);
tftp_send_oack(sockfd, sa, options,
data->data_buffer, data->data_buffer_size);
/* Proceed normally with the next client,
going to OACK state */
logger(LOG_INFO,
"Serving next client: %s:%d",
inet_ntoa(client_info->client.sin_addr),
ntohs(client_info->client.sin_port));
sa = &client_info->client;
state = S_SEND_OACK;
break;
}
else if (client_info == NULL)
{
/* we got a big problem if this happend */
logger(LOG_ERR,
"%s: %d: abnormal condition",
__FILE__, __LINE__);
state = S_ABORT;
break;
}
}
logger(LOG_WARNING, "timeout: retrying...");
state = timeout_state;
}
break;
case GET_ACK:
/* handle case where packet come from un unexpected client */
if (multicast)
{
if ((sa->sin_port != from.sin_port) ||
(sa->sin_addr.s_addr != from.sin_addr.s_addr))
{
/* We got an ACK from a client that is not the master client.
* If this is an ACK for the last block, mark this client as
* done
*/
if ((last_block != -1) && (block_number > last_block))
{
if (tftpd_clientlist_done(data, NULL, &from) == 1)
logger(LOG_DEBUG, "client done <%s>",
inet_ntoa(client_info->client.sin_addr));
else
logger(LOG_WARNING, "packet discarded <%s:%d>",
inet_ntoa(from.sin_addr), ntohs(from.sin_port));
}
else
/* If not, send and OACK with mc=0 to shut it up. */
{
opt_options_to_string(options,
string, MAXLEN);
if (data->trace)
logger(LOG_DEBUG, "sent OACK <%s>", string);
tftp_send_oack(sockfd, &from, options,
data->data_buffer, data->data_buffer_size);
}
break;
}
}
else
{
/* check that the packet is from the current client */
if (sa->sin_port != from.sin_port)
{
if (data->checkport)
{
logger(LOG_WARNING, "packet discarded <%s:%d>",
inet_ntoa(from.sin_addr), ntohs(from.sin_port));
break;
}
else
logger(LOG_WARNING,
"source port mismatch, check bypassed");
}
}
/* The ACK is from the current client */
number_of_timeout = 0;
block_number = ntohs(tftphdr->th_block);
if (data->trace)
logger(LOG_DEBUG, "received ACK <block: %d>",
block_number);
if ((last_block != -1) && (block_number > last_block))
{
state = S_END;
break;
}
state = S_SEND_DATA;
break;
case GET_ERROR:
/* handle case where packet come from un unexpected client */
if (multicast)
{
/* if packet is not from the current master client */
if ((sa->sin_port != from.sin_port) ||
(sa->sin_addr.s_addr != from.sin_addr.s_addr))
{
/* mark this client done */
if (tftpd_clientlist_done(data, NULL, &from) == 1)
{
if (data->trace)
logger(LOG_DEBUG, "client sent ERROR, mark as done <%s>",
inet_ntoa(client_info->client.sin_addr));
}
else
logger(LOG_WARNING, "packet discarded <%s>",
inet_ntoa(from.sin_addr));
/* current state is unchanged */
break;
}
}
else
{
/* check that the packet is from the current client */
if (sa->sin_port != from.sin_port)
{
if (data->checkport)
{
logger(LOG_WARNING, "packet discarded <%s>",
inet_ntoa(from.sin_addr));
break;
}
else
logger(LOG_WARNING,
"source port mismatch, check bypassed");
}
}
/* Got an ERROR from the current master client */
Strncpy(string, tftphdr->th_msg,
(((data_size - 4) > MAXLEN) ? MAXLEN :
(data_size - 4)));
if (data->trace)
logger(LOG_DEBUG, "received ERROR <code: %d, msg: %s>",
ntohs(tftphdr->th_code), string);
if (multicast)
{
logger(LOG_DEBUG, "Marking client as done");
state = S_END;
}
else
state = S_ABORT;
break;
case GET_DISCARD:
/* FIXME: should we increment number_of_timeout */
logger(LOG_WARNING, "packet discarded <%s>",
inet_ntoa(from.sin_addr));
break;
case ERR:
logger(LOG_ERR, "%s: %d: recvfrom: %s",
__FILE__, __LINE__, strerror(errno));
state = S_ABORT;
break;
default:
logger(LOG_ERR, "%s: %d: abnormal return value %d",
__FILE__, __LINE__, result);
}
break;
case S_END:
if (multicast)
{
logger(LOG_DEBUG, "End of multicast transfer");
/* mark the current client done */
tftpd_clientlist_done(data, client_info, NULL);
/* Look if there is another client to serve. We lock list of
client to make sure no other thread try to add clients in
our back */
if (tftpd_clientlist_next(data, &client_info) == 1)
{
logger(LOG_INFO,
"Serving next client: %s:%d",
inet_ntoa(client_info->client.sin_addr),
ntohs(client_info->client.sin_port));
/* client is a new client structure */
sa = &client_info->client;
/* nedd to send an oack to that client */
state = S_SEND_OACK;
fseek(fp, 0, SEEK_SET);
}
else
{
logger(LOG_INFO, "No more client, end of tranfers");
fclose(fp);
return OK;
}
}
else
{
logger(LOG_DEBUG, "End of transfer");
fclose(fp);
return OK;
}
break;
case S_ABORT:
logger(LOG_DEBUG, "Aborting transfer");
fclose(fp);
return ERR;
default:
fclose(fp);
logger(LOG_ERR, "%s: %d: abnormal condition",
__FILE__, __LINE__);
return ERR;
}
}
}

View File

@ -0,0 +1,350 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* tftpd_list.c
* thread_data and client list related functions
*
* $Id: tftpd_list.c,v 1.9 2004/02/27 02:05:26 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "tftpd.h"
#include "logger.h"
/*
* thread_data is a double link list of server threads. Server threads
* are started by the main thread when a new request arrives. The number
* of threads running is held in number_of_thread. Any access to the
* thread_data list: insertion and extraction of elements must be protected
* by the mutex. This is done in this file by all functions playing with the thread list.
* Note that individual threads do not need to lock the list when playing in their data.
* See tftpd.h.
*
* In addition, it is needed to use mutex when working on the client list of
* individual thread. In some case, the thread data mutex is needed also.
* Again, the functions in this file take care of this.
*/
struct thread_data *thread_data = NULL; /* head of thread list */
static int number_of_thread = 0;
pthread_mutex_t thread_list_mutex = PTHREAD_MUTEX_INITIALIZER;
/*
* Add a new thread_data structure to the list. Thread list mutex is locked
* before walking the list and doing the insertion.
*/
int tftpd_list_add(struct thread_data *new)
{
struct thread_data *current = thread_data;
int ret;
pthread_mutex_lock(&thread_list_mutex);
number_of_thread++;
ret = number_of_thread;
if (thread_data == NULL)
{
thread_data = new;
new->prev = NULL;
new->next = NULL;
}
else
{
while (current->next != NULL)
current = current->next;
current->next = new;
new->prev = current;
new->next = NULL;
}
pthread_mutex_unlock(&thread_list_mutex);
return ret;
}
/*
* Remove a thread_data structure from the list.
*/
int tftpd_list_remove(struct thread_data *old)
{
struct thread_data *current = thread_data;
int ret;
pthread_mutex_lock(&thread_list_mutex);
number_of_thread--;
ret = number_of_thread;
if (thread_data == old)
{
if (thread_data->next != NULL)
{
thread_data = thread_data->next;
thread_data->prev = NULL;
}
else
thread_data = NULL;
}
else
{
while (current != old)
current = current->next;
if (current->next && current->prev)
{
current->prev->next = current->next;
current->next->prev = current->prev;
}
else
current->prev->next = NULL;
}
pthread_mutex_unlock(&thread_list_mutex);
return ret;
}
/*
* Return the number of threads actually started.
*/
int tftpd_list_num_of_thread(void)
{
int ret;
pthread_mutex_lock(&thread_list_mutex);
ret = number_of_thread;
pthread_mutex_unlock(&thread_list_mutex);
return ret;
}
/*
* This function looks for a thread serving exactly the same
* file and with the same options as another client. This implies a
* multicast enabled client.
*/
int tftpd_list_find_multicast_server_and_add(struct thread_data **thread,
struct thread_data *data,
struct client_info *client)
{
struct thread_data *current = thread_data; /* head of the list */
struct tftp_opt *tftp_options = data->tftp_options;
struct client_info *tmp;
char options[MAXLEN];
char string[MAXLEN];
char *index;
int len;
*thread = NULL;
opt_request_to_string(tftp_options, options, MAXLEN);
index = strstr(options, "multicast");
len = (int)index - (int)options;
/* lock the whole list before walking it */
pthread_mutex_lock(&thread_list_mutex);
while (current)
{
if (current != data)
{
/* Lock the client list before reading client ready state */
pthread_mutex_lock(&current->client_mutex);
if (current->client_ready == 1)
{
opt_request_to_string(current->tftp_options, string, MAXLEN);
/* must have exact same option string */
if (strncmp(string, options, len) == 0)
{
*thread = current;
/* insert the new client at the end. If the client is already
in the list, don't add it again. */
tmp = current->client_info;
while (1)
{
if ((tmp->client.sin_port == client->client.sin_port) &&
(tmp->client.sin_addr.s_addr == client->client.sin_addr.s_addr) &&
(tmp->done == 0))
{
/* unlock mutex and exit */
pthread_mutex_unlock(&current->client_mutex);
pthread_mutex_unlock(&thread_list_mutex);
return 2;
}
if (tmp->next == NULL)
break;
tmp = tmp->next;
}
tmp->next = client;
/* unlock mutex and exit */
pthread_mutex_unlock(&current->client_mutex);
pthread_mutex_unlock(&thread_list_mutex);
return 1;
}
}
pthread_mutex_unlock(&current->client_mutex);
}
current = current->next;
}
pthread_mutex_unlock(&thread_list_mutex);
return 0;
}
inline void tftpd_clientlist_ready(struct thread_data *thread)
{
pthread_mutex_lock(&thread->client_mutex);
thread->client_ready = 1;
pthread_mutex_unlock(&thread->client_mutex);
}
/*
* As of the the above comment, we never remove the head element.
*/
void tftpd_clientlist_remove(struct thread_data *thread,
struct client_info *client)
{
struct client_info *tmp = thread->client_info;
pthread_mutex_lock(&thread->client_mutex);
while ((tmp->next != client) && (tmp->next != NULL))
tmp = tmp->next;
if (tmp->next == NULL)
return;
tmp->next = tmp->next->next;
pthread_mutex_unlock(&thread->client_mutex);
}
/*
* Free all allocated client structure.
*/
void tftpd_clientlist_free(struct thread_data *thread)
{
struct client_info *tmp;
struct client_info *head = thread->client_info;
pthread_mutex_lock(&thread->client_mutex);
while (head)
{
tmp = head;
head = head->next;
free(tmp);
}
pthread_mutex_unlock(&thread->client_mutex);
}
/*
* Mark a client as done
*/
int tftpd_clientlist_done(struct thread_data *thread,
struct client_info *client,
struct sockaddr_in *sock)
{
struct client_info *head = thread->client_info;
pthread_mutex_lock(&thread->client_mutex);
if (client)
{
client->done = 1;
pthread_mutex_unlock(&thread->client_mutex);
return 1;
}
if (sock)
{
/* walk the list to find this client */
while (head)
{
if (memcmp(sock, &head->client, sizeof(struct sockaddr_in)) == 0)
{
head->done = 1;
pthread_mutex_unlock(&thread->client_mutex);
return 1;
}
else
head = head->next;
}
}
pthread_mutex_unlock(&thread->client_mutex);
return 0;
}
/*
* Return the next client in the list.
* If a client is found, *client got the address and return 1.
* If no more client are available, 0 is returned and client_ready is unset
* If the new client is the same, -1 is returned.
*
* List is search from the current client address and wrap around.
*/
int tftpd_clientlist_next(struct thread_data *thread,
struct client_info **client)
{
struct client_info *tmp;
pthread_mutex_lock(&thread->client_mutex);
/* If there's a next, start with it. Else search from the
beginning */
if ((*client)->next)
tmp = (*client)->next;
else
tmp = thread->client_info;
/* search from the current client */
while (tmp)
{
if (!tmp->done)
{
/* If this is the same as *client */
if (tmp == *client)
{
pthread_mutex_unlock(&thread->client_mutex);
return -1;
}
*client = tmp;
pthread_mutex_unlock(&thread->client_mutex);
return 1;
}
tmp = tmp->next;
/* if no more entry, start at begining */
if (tmp == NULL)
tmp = thread->client_info;
/* if we fall back on the current client, there is no more
client to serve */
if (tmp == *client)
tmp = NULL;
}
/* There is no more client to server */
thread->client_ready = 0;
*client = NULL;
pthread_mutex_unlock(&thread->client_mutex);
return 0;
}
void tftpd_list_kill_threads(void)
{
struct thread_data *current = thread_data; /* head of list */
pthread_mutex_lock(&thread_list_mutex);
while (current != NULL)
{
pthread_kill(current->tid, SIGTERM);
current = current->next;
}
pthread_mutex_unlock(&thread_list_mutex);
}

View File

@ -0,0 +1,351 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* tftpd_mcast.c
* support routine for multicast server
*
* $Id: tftpd_mcast.c,v 1.6 2003/04/25 00:16:19 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "tftpd.h"
#include "tftp_def.h"
#include "logger.h"
#define START 0
#define GET_ENUM 1
#define GET_GRP 2
#define EXPAND 3
pthread_mutex_t mcast_tid_list = PTHREAD_MUTEX_INITIALIZER;
int parse_ip(char *string, char **res_ip);
int parse_port(char *string, char **res_port);
struct tid {
char *addr;
short port;
int used;
struct tid *next;
};
struct tid *tid_list = NULL;
/*
* Return a free IP/Port for the multicast transfer
*/
int tftpd_mcast_get_tid(char **addr, short *port)
{
struct tid *current = tid_list;
pthread_mutex_lock(&mcast_tid_list);
/* walk the list for a free tid */
while (current != NULL)
{
if (current->used == 0)
{
*addr = current->addr;
*port = current->port;
current->used = 1;
pthread_mutex_unlock(&mcast_tid_list);
return OK;
}
else
current = current->next;
}
pthread_mutex_unlock(&mcast_tid_list);
return ERR;
}
int tftpd_mcast_free_tid(char *addr, short port)
{
struct tid *current = tid_list;
pthread_mutex_lock(&mcast_tid_list);
while (current != NULL)
{
if ((current->used == 1) && (current->port == port) &&
(strcmp(current->addr, addr) == 0))
{
current->used = 0;
pthread_mutex_unlock(&mcast_tid_list);
return OK;
}
else
current = current->next;
}
pthread_mutex_unlock(&mcast_tid_list);
return ERR;
}
/* valid address specification:
239.255.0.0-239.255.0.20
239.255.0.0-20
239.255.0.0,1,2,3,8,10
*/
/* valid port specification
1758
1758-1780
1758,1760,4000
*/
int tftpd_mcast_parse_opt(char *addr, char *ports)
{
char *ip;
char *port;
struct tid *current = NULL;
struct tid *tmp = NULL;
while (1)
{
if (parse_ip(addr, &ip) != OK)
return ERR;
if (ip == NULL)
return OK;
while (1)
{
if (parse_port(ports, &port) != OK)
return ERR;
if (port == NULL)
break;
/* add this ip/port to the tid list */
tmp = malloc(sizeof(struct tid));
tmp->addr = strdup(ip);
tmp->port = (short)atoi(port);
tmp->used = 0;
tmp->next = NULL;
if (tid_list == NULL)
{
tid_list = tmp;
current = tid_list;
}
else
{
current->next = tmp;
current = tmp;
}
}
}
}
void tftpd_mcast_clean(void)
{
}
int parse_ip(char *string, char **res_ip)
{
static int state = START;
static char s[MAXLEN];
static char *saveptr;
static char s2[MAXLEN];
static char *saveptr2;
static int ip[4];
static int tmp_ip[4];
static int i;
int res;
char *tmp = NULL, *tmp2 = NULL;
static char out[MAXLEN];
*res_ip = NULL;
while (1)
{
switch (state)
{
case START:
Strncpy(s, string, MAXLEN);
tmp = strtok_r(s, ",", &saveptr);
if (tmp == NULL)
{
state = START;
return ERR;
}
else
state = GET_GRP;
break;
case GET_ENUM:
tmp = strtok_r(NULL, ",", &saveptr);
if (tmp == NULL)
{
state = START;
return OK;
}
else
state = GET_GRP;
break;
case GET_GRP:
Strncpy(s2, tmp, MAXLEN);
tmp = strtok_r(s2, "-", &saveptr2);
if (tmp == NULL)
{
state = START;
return ERR;
}
res = sscanf(tmp, "%d.%d.%d.%d", &tmp_ip[0], &tmp_ip[1],
&tmp_ip[2], &tmp_ip[3]);
if (res == 4)
{
for (i=0; i < 4; i++)
ip[i] = tmp_ip[i];
}
else
{
if (res == 1)
{
ip[3] = tmp_ip[0];
}
else
{
state = START;
return ERR;
}
}
tmp2 = strtok_r(NULL, "-", &saveptr2);
if (tmp2 == NULL)
{
state = GET_ENUM;
snprintf(out, sizeof(out), "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
*res_ip = out;
return OK;
}
else
{
sscanf(tmp2, "%d", &tmp_ip[0]);
i = ip[3];
if (i >= tmp_ip[0])
{
logger(LOG_ERR, "Bad address range: %d.%d.%d.%d-%d",
ip[0], ip[1], ip[2], ip[3], tmp_ip[0]);
return ERR;
}
state = EXPAND;
}
break;
case EXPAND:
if (i > tmp_ip[0])
{
state = GET_ENUM;
break;
}
snprintf(out, sizeof(out), "%d.%d.%d.%d", ip[0], ip[1], ip[2], i);
i++;
*res_ip = out;
return OK;
break;
}
}
}
int parse_port(char *string, char **res_port)
{
static int state = START;
static char s[MAXLEN];
static char *saveptr;
static char s2[MAXLEN];
static char *saveptr2;
static int port;
static int tmp_port;
static int i;
int res;
char *tmp = NULL, *tmp2 = NULL;
static char out[MAXLEN];
*res_port = NULL;
while (1)
{
switch (state)
{
case START:
Strncpy(s, string, MAXLEN);
tmp = strtok_r(s, ",", &saveptr);
if (tmp == NULL)
{
state = START;
return ERR;
}
else
state = GET_GRP;
break;
case GET_ENUM:
tmp = strtok_r(NULL, ",", &saveptr);
if (tmp == NULL)
{
state = START;
return OK;
}
else
state = GET_GRP;
break;
case GET_GRP:
Strncpy(s2, tmp, MAXLEN);
tmp = strtok_r(s2, "-", &saveptr2);
if (tmp == NULL)
{
state = START;
return ERR;
}
res = sscanf(tmp, "%d", &port);
if (res != 1)
{
state = START;
return ERR;
}
tmp2 = strtok_r(NULL, "-", &saveptr2);
if (tmp2 == NULL)
{
state = GET_ENUM;
snprintf(out, sizeof(out), "%d", port);
*res_port = out;
return OK;
}
else
{
sscanf(tmp2, "%d", &tmp_port);
i = port;
if (i >= tmp_port)
{
logger(LOG_ERR, "Bad port range: %d-%d", i, tmp_port);
return ERR;
}
state = EXPAND;
}
break;
case EXPAND:
if (i > tmp_port)
{
state = GET_ENUM;
break;
}
snprintf(out, sizeof(out), "%d", i);
i++;
*res_port = out;
return OK;
break;
}
}
}

View File

@ -0,0 +1,678 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* tftpd_mtftp.c
* mtftp support for atftp
*
* $Id: tftpd_mtftp.c,v 1.12 2004/02/27 02:05:26 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include "config.h"
#if HAVE_MTFTP
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <signal.h>
#ifdef HAVE_WRAP
#include <tcpd.h>
#endif
#include "tftp_io.h"
#include "tftp_def.h"
#include "logger.h"
#include "tftpd.h"
#include "tftpd_mtftp.h"
#define S_BEGIN 0
#define S_SEND_DATA 4
#define S_WAIT_PACKET 5
#define S_REQ_RECEIVED 6
#define S_ABORT 10
#define S_END 11
#define S_EXIT 12
/* function used in this file only */
int tftpd_mtftp_thread_add(struct mtftp_data *data, struct mtftp_thread *thread);
struct mtftp_thread *tftpd_mtftp_find_server(struct mtftp_data *data, char *filename);
int tftpd_mtftp_unique(struct mtftp_data *data, char *filename, char *ip, char *port);
void *tftpd_mtftp_send_file(void *arg);
/* read only except for the main thread */
extern int tftpd_cancel;
/*
* This function parse the configuration file and create data structure
* Format is:
* filename mcast_ip client_port
* one entry per line
*/
struct mtftp_data *tftpd_mtftp_init(char *filename)
{
FILE *fp;
struct mtftp_data *data = NULL;
struct mtftp_thread *thread = NULL;
char string[MAXLEN];
char *token;
int line = 0;
struct stat file_stat;
struct hostent host;
/* open file */
if ((fp = fopen(filename, "r")) == NULL)
{
logger(LOG_ERR, "mtftp: failed to open configuration file, continuing");
return NULL;
}
logger(LOG_DEBUG, "mtftp: opened configuration file %s", filename);
/* allocate memory */
if ((data = malloc(sizeof(struct mtftp_data))) == NULL)
{
logger(LOG_ERR, "%s: %d: Memory allocation failed",
__FILE__, __LINE__);
fclose(fp);
return NULL;
}
/* Allocate memory for tftp option structure. */
if ((data->tftp_options =
malloc(sizeof(tftp_default_options))) == NULL)
{
logger(LOG_ERR, "%s: %d: Memory allocation failed",
__FILE__, __LINE__);
tftpd_mtftp_clean(data);
return NULL;
}
/* Allocate data buffer for tftp transfer. */
if ((data->data_buffer = malloc((size_t)SEGSIZE + 4)) == NULL)
{
logger(LOG_ERR, "%s: %d: Memory allocation failed",
__FILE__, __LINE__);
tftpd_mtftp_clean(data);
return NULL;
}
data->data_buffer_size = SEGSIZE + 4;
/* Copy default options. */
memcpy(data->tftp_options, tftp_default_options,
sizeof(tftp_default_options));
data->tftp_options[OPT_TIMEOUT].enabled = 0;
data->tftp_options[OPT_TSIZE].enabled = 0;
data->tftp_options[OPT_BLKSIZE].enabled = 0;
data->tftp_options[OPT_MULTICAST].enabled = 0;
/* init data structure */
data->thread_data = NULL;
data->number_of_thread = 0;
logger(LOG_DEBUG, "mtftp options: ");
/* parse the file */
while (fgets(string, MAXLEN, fp) != NULL)
{
line++;
if (thread == NULL)
{
/* allocate memory */
if ((thread = calloc(1, sizeof(struct mtftp_thread))) == NULL)
{
logger(LOG_ERR, "%s: %d: Memory allocation failed",
__FILE__, __LINE__);
fclose(fp);
tftpd_mtftp_clean(data);
return NULL;
}
/* Allocate data buffer for tftp transfer. */
if ((thread->data_buffer = malloc((size_t)SEGSIZE + 4)) == NULL)
{
logger(LOG_ERR, "%s: %d: Memory allocation failed",
__FILE__, __LINE__);
tftpd_mtftp_clean(data);
return NULL;
}
thread->data_buffer_size = SEGSIZE + 4;
/* Add this thread to the list */
tftpd_mtftp_thread_add(data, thread);
}
/* Parse the line */
token = strtok(string, " ");
if (!token)
continue;
if (token[0] == '#') /* if first char is #, this is a comment */
continue;
Strncpy(thread->file_name, token, MAXLEN);
token = strtok(NULL, " ");
if (!token)
continue;
Strncpy(thread->mcast_ip, token, MAXLEN);
token = strtok(NULL, " ");
if (!token)
continue;
Strncpy(thread->client_port, token, MAXLEN);
/* validate arguements */
/* file name verification */
if (tftpd_rules_check(thread->file_name) != OK)
{
logger(LOG_WARNING, "mtftp: file name rules violated %s (%s line %d)",
thread->file_name, filename, line);
continue;
}
/* open file */
if ((thread->fp = fopen(thread->file_name, "r")) == NULL)
{
logger(LOG_WARNING, "mtftp: can't open file %s (%s line %d)",
thread->file_name,
filename, line);
continue;
}
/* verify file size */
fstat(fileno(thread->fp), &file_stat);
if ((file_stat.st_size / SEGSIZE) > 65535)
{
logger(LOG_WARNING, "mtftp: file %s too big (%s line %d)", thread->file_name,
filename, line);
fclose(thread->fp);
continue;
}
/* verify port */
thread->mcast_port = atoi(thread->client_port);
if ((thread->mcast_port < 0) || (thread->mcast_port > 65535))
{
logger(LOG_WARNING, "mtftp: bad port number %d (%s line %d)",
thread->client_port, filename, line);
fclose(thread->fp);
continue;
}
/* verify IP is valid */
if (Gethostbyname(thread->mcast_ip, &host) == OK)
{
thread->sa_mcast.sin_family = host.h_addrtype;
memcpy(&thread->sa_mcast.sin_addr.s_addr,
host.h_addr_list[0], host.h_length);
thread->sa_mcast.sin_port = htons(thread->mcast_port);
if (!IN_MULTICAST(ntohl(thread->sa_mcast.sin_addr.s_addr)))
{
logger(LOG_WARNING, "mtftp: bad multicast address %s\n",
thread->mcast_ip);
fclose(thread->fp);
continue;
}
}
/* verify IP/port is unique */
if (tftpd_mtftp_unique(data, thread->file_name, thread->mcast_ip, thread->client_port))
{
logger(LOG_INFO, "mtftp: duplicate server (%s line %d)", filename, line);
continue;
}
/* some useful info */
logger(LOG_INFO, "mtftp: will serve %s on %s port %s", thread->file_name,
thread->mcast_ip, thread->client_port);
/* next loop we must allocate a new structure */
thread = NULL;
}
fclose(fp);
return data;
}
int tftpd_mtftp_clean(struct mtftp_data *data)
{
struct mtftp_thread *thread = data->thread_data;
struct mtftp_thread *tmp = data->thread_data;
if (data == NULL)
return OK;
/* clean all thread data structure */
while (thread)
{
/* point to the next item before freeing memory */
tmp = thread->next;
/* free everything */
if (thread->fp)
fclose(thread->fp);
if (thread->data_buffer)
free(thread->data_buffer);
free(thread);
thread = tmp;
}
/* clean mtftp_data structure */
if (data->data_buffer)
free(data->data_buffer);
if (data->tftp_options)
free(data->tftp_options);
free(data);
return OK;
}
/*
* Add a mtftp_thread structure to the list. I guest this could use the code in
* tftpd_list.c, but I prefer keeping it appart for now.
*/
int tftpd_mtftp_thread_add(struct mtftp_data *data, struct mtftp_thread *thread)
{
struct mtftp_thread *tmp = data->thread_data;
if (data->thread_data == NULL)
{
data->thread_data = thread;
return OK;
}
while (tmp->next != NULL)
tmp = tmp->next;
tmp->next = thread;
return OK;
}
void tftpd_mtftp_kill_threads(struct mtftp_data *data)
{
struct mtftp_thread *current = data->thread_data; /* head of list */
while (current != NULL)
{
/* kill running threads */
if (current->running && current->tid)
pthread_kill(current->tid, SIGTERM);
current = current->next;
}
}
struct mtftp_thread *tftpd_mtftp_find_server(struct mtftp_data *data, char *filename)
{
struct mtftp_thread *tmp = data->thread_data;
while (tmp)
{
if (strcmp(filename, tmp->file_name) == 0)
return tmp;
tmp = tmp->next;
}
return NULL;
}
int tftpd_mtftp_unique(struct mtftp_data *data, char *filename, char *ip, char *port)
{
struct mtftp_thread *tmp = data->thread_data;
while (tmp->next)
{
if (strcmp(filename, tmp->file_name) == 0)
return 1;
if (strcmp(ip, tmp->mcast_ip) == 0)
if (strcmp(port, tmp->client_port) == 0)
return 1;
tmp = tmp->next;
}
return 0;
}
/*
* This thread listen to the specified port for read request. If the requested
* file as been specified in the mtftp.conf file and the server is not currently
* serving this file, spawn the serving thread.
*/
void *tftpd_mtftp_server(void *arg)
{
fd_set rfds; /* for select */
struct mtftp_data *data = (struct mtftp_data *)arg;
struct mtftp_thread *thread;
int sockfd;
struct sockaddr_in sa;
socklen_t len = sizeof(struct sockaddr);
#ifdef HAVE_WRAP
char client_addr[16];
#endif
int retval; /* hold return value for testing */
int data_size; /* returned size by recvfrom */
char filename[MAXLEN];
char string[MAXLEN]; /* hold the string we pass to the logger */
logger(LOG_NOTICE, "mtftp main server thread started");
/* initialise sockaddr_in structure */
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = htonl(INADDR_ANY);
sa.sin_port = htons(data->server_port);
/* open the socket */
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == 0)
{
logger(LOG_ERR, "mtftp: can't open socket");
pthread_exit(NULL);
}
/* bind the socket to the tftp port */
if (bind(sockfd, (struct sockaddr*)&sa, sizeof(sa)) < 0)
{
logger(LOG_ERR, "mtftp: can't bind port");
pthread_exit(NULL);
}
while (!tftpd_cancel)
{
FD_ZERO(&rfds);
FD_SET(sockfd, &rfds);
select(sockfd + 1, &rfds, NULL, NULL, NULL);
if (FD_ISSET(sockfd, &rfds) && (!tftpd_cancel))
{
/* read the data packet and verify it's a RRQ and a thread exist for
that file name */
memset(&sa, 0, sizeof(sa)); /* this will hold the client info */
data_size = data->data_buffer_size;
retval = tftp_get_packet(sockfd, -1, NULL, &sa, NULL, NULL,
data->timeout,
&data_size, data->data_buffer);
#ifdef HAVE_WRAP
/* Verify the client has access. We don't look for the name but
rely only on the IP address for that. */
inet_ntop(AF_INET, &sa.sin_addr,
client_addr, sizeof(client_addr));
if (hosts_ctl("in.tftpd", STRING_UNKNOWN, client_addr,
STRING_UNKNOWN) == 0)
{
logger(LOG_ERR, "mtftp: connection refused from %s", client_addr);
continue;
}
#endif
/* read options from this request */
opt_parse_request(data->data_buffer, data_size,
data->tftp_options);
opt_request_to_string(data->tftp_options, string, MAXLEN);
Strncpy(filename, data->tftp_options[OPT_FILENAME].value,
MAXLEN);
/* verify this is a RRQ */
if (retval != GET_RRQ)
{
logger(LOG_WARNING, "unsupported request <%d> from %s",
retval, inet_ntoa(sa.sin_addr));
tftp_send_error(sockfd, &sa, EBADOP, data->data_buffer, data->data_buffer_size);
if (data->trace)
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EBADOP,
tftp_errmsg[EBADOP]);
continue;
}
else
{
logger(LOG_NOTICE, "Serving %s to %s:%d", filename,
inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
if (data->trace)
logger(LOG_DEBUG, "received RRQ <%s>", string);
}
/* validity check, only octet mode supported */
if (strcasecmp(data->tftp_options[OPT_MODE].value, "octet") != 0)
{
logger(LOG_WARNING, "mtftp: support only octet mode");
continue;
}
/* file name verification */
if (tftpd_rules_check(filename) != OK)
{
logger(LOG_WARNING, "mtftp: file name rules violated %s", filename);
continue;
}
/* find server for this file*/
if ((thread = tftpd_mtftp_find_server(data, filename)) == NULL)
{
logger(LOG_WARNING, "mtftp: no server found for file %s", filename);
continue;
}
if (thread->running)
{
logger(LOG_NOTICE, "mtftp: already serving this file");
continue;
}
/* copy client info for server */
memcpy(&thread->sa_in, &sa, sizeof(struct sockaddr_in));
/* open a socket for client communication */
if ((thread->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == 0)
{
logger(LOG_ERR, "mtftp: can't open socket");
pthread_exit(NULL);
}
getsockname(sockfd, (struct sockaddr *)&(sa), &len);
//memset(&sa, 0, sizeof(sa));
sa.sin_port = 0;
/* bind the socket to the tftp port */
if (bind(thread->sockfd, (struct sockaddr*)&sa, sizeof(sa)) < 0)
{
logger(LOG_ERR, "mtftp: can't bind port");
pthread_exit(NULL);
}
getsockname(thread->sockfd, (struct sockaddr *)&(sa), &len);
/* configure multicast socket */
thread->mcastaddr.imr_multiaddr.s_addr = thread->sa_mcast.sin_addr.s_addr;
thread->mcastaddr.imr_interface.s_addr = htonl(INADDR_ANY);
setsockopt(thread->sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
&data->mcast_ttl, sizeof(data->mcast_ttl));
/* give server thread access to mtftp options */
thread->mtftp_data = data;
/* mask the thread as running */
thread->running = 1;
/* spawn the new thread */
/* Start a new server thread. */
if (pthread_create(&thread->tid, NULL, tftpd_mtftp_send_file,
(void *)thread) != 0)
{
logger(LOG_ERR, "mtftp: failed to start new thread");
thread->running = 0;
}
}
}
tftpd_mtftp_clean(data);
logger(LOG_NOTICE, "mtftp main server thread exiting");
pthread_exit(NULL);
}
void *tftpd_mtftp_send_file(void *arg)
{
int state = S_BEGIN;
int timeout_state = state;
int result;
int block_number = 0;
int last_block = -1;
int data_size;
struct mtftp_thread *data = (struct mtftp_thread *)arg;
struct sockaddr_in *sa = &data->sa_in;
struct sockaddr_in from;
int sockfd = data->sockfd;
struct tftphdr *tftphdr = (struct tftphdr *)data->data_buffer;
char string[MAXLEN];
int number_of_timeout = 0;
/* Detach ourself. That way the main thread does not have to
* wait for us with pthread_join. */
pthread_detach(pthread_self());
/* sockets are opened and every as been initialised for us,
just proceed */
while (1)
{
if (tftpd_cancel)
{
logger(LOG_DEBUG, "thread cancelled");
tftp_send_error(sockfd, sa, EUNDEF, data->data_buffer, data->data_buffer_size);
if (data->mtftp_data->trace)
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EUNDEF,
tftp_errmsg[EUNDEF]);
state = S_ABORT;
}
switch (state)
{
case S_BEGIN:
/* The first data packet as to be sent to the unicast address
of the client */
timeout_state = state;
fseek(data->fp, block_number * (data->data_buffer_size - 4),
SEEK_SET);
/* read data from file */
data_size = fread(tftphdr->th_data, 1,
data->data_buffer_size - 4, data->fp) + 4;
/* record the last block number */
if (feof(data->fp))
last_block = block_number;
/* send data to unicast address */
tftp_send_data(sockfd, sa, block_number + 1,
data_size, data->data_buffer);
if (data->mtftp_data->trace)
logger(LOG_DEBUG, "sent DATA <block: %d, size %d>",
block_number + 1, data_size - 4);
state = S_WAIT_PACKET;
break;
case S_SEND_DATA:
timeout_state = state;
fseek(data->fp, block_number * (data->data_buffer_size - 4),
SEEK_SET);
/* read data from file */
data_size = fread(tftphdr->th_data, 1,
data->data_buffer_size - 4, data->fp) + 4;
/* record the last block number */
if (feof(data->fp))
last_block = block_number;
/* send data to multicast address */
tftp_send_data(sockfd, &data->sa_mcast,
block_number + 1, data_size,
data->data_buffer);
if (data->mtftp_data->trace)
logger(LOG_DEBUG, "sent DATA <block: %d, size %d>",
block_number + 1, data_size - 4);
state = S_WAIT_PACKET;
break;
case S_WAIT_PACKET:
data_size = data->data_buffer_size;
result = tftp_get_packet(sockfd, -1, NULL, sa, &from, NULL,
data->mtftp_data->timeout,
&data_size, data->data_buffer);
switch (result)
{
case GET_TIMEOUT:
number_of_timeout++;
if (number_of_timeout > NB_OF_RETRY)
{
logger(LOG_INFO, "client (%s) not responding",
inet_ntoa(data->sa_in.sin_addr));
state = S_END;
break;
}
logger(LOG_WARNING, "timeout: retrying...");
state = timeout_state;
break;
case GET_ACK:
if (sa->sin_port != from.sin_port)
{
logger(LOG_WARNING, "packet discarded");
break;
}
/* handle case where packet come from un unexpected client */
if ((sa->sin_port == from.sin_port) &&
(sa->sin_addr.s_addr == from.sin_addr.s_addr))
{
/* The ACK is from the exected client */
number_of_timeout = 0;
block_number = ntohs(tftphdr->th_block);
if (data->mtftp_data->trace)
logger(LOG_DEBUG, "received ACK <block: %d>",
block_number);
if ((last_block != -1) && (block_number > last_block))
{
state = S_END;
break;
}
state = S_SEND_DATA;
}
break;
case GET_ERROR:
if (sa->sin_port != from.sin_port)
{
logger(LOG_WARNING, "packet discarded");
break;
}
/* handle case where packet come from un unexpected client */
if ((sa->sin_port == from.sin_port) &&
(sa->sin_addr.s_addr == from.sin_addr.s_addr))
{
/* Got an ERROR from the current master client */
Strncpy(string, tftphdr->th_msg,
(((data_size - 4) > MAXLEN) ? MAXLEN :
(data_size - 4)));
if (data->mtftp_data->trace)
logger(LOG_DEBUG, "received ERROR <code: %d, msg: %s>",
ntohs(tftphdr->th_code), string);
state = S_ABORT;
}
break;
case GET_DISCARD:
logger(LOG_WARNING, "packet discarded");
break;
case ERR:
logger(LOG_ERR, "%s: %d: recvfrom: %s",
__FILE__, __LINE__, strerror(errno));
state = S_ABORT;
break;
default:
logger(LOG_ERR, "%s: %d: abnormal return value %d",
__FILE__, __LINE__, result);
}
break;
case S_END:
logger(LOG_DEBUG, "End of transfer");
state = S_EXIT;
break;
case S_ABORT:
logger(LOG_DEBUG, "Aborting transfer");
state = S_EXIT;
break;
case S_EXIT:
data->running = 0;
data->tid = 0;
pthread_exit(NULL);
default:
logger(LOG_ERR, "%s: %d: abnormal condition",
__FILE__, __LINE__);
state = S_EXIT;
}
}
}
#endif

View File

@ -0,0 +1,85 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* tftpd_mtftp.h
*
* $Id: tftpd_mtftp.h,v 1.5 2004/02/27 02:05:26 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef tftpd_mtftp_h
#define tftpd_mtftp_h
#include <pthread.h>
#include <arpa/tftp.h>
#include <arpa/inet.h>
#include "tftp_io.h"
#include "options.h"
/*
* This structure hold information on mtftp configuration
*
*/
struct mtftp_data {
struct mtftp_thread *thread_data;
int number_of_thread;
/* for receiving of initial request */
char *data_buffer;
int data_buffer_size;
struct tftp_opt *tftp_options;
/* options scanned from command line */
int server_port;
int mcast_ttl;
int timeout;
int checkport;
int trace;
};
struct mtftp_thread {
pthread_t tid;
/* Configuration data */
int running;
char file_name[MAXLEN];
char mcast_ip[MAXLEN]; /* FIXME: could be less memory */
char client_port[MAXLEN];
/* Server thread variables */
FILE *fp;
int sockfd;
struct sockaddr_in sa_in;
struct sockaddr_in sa_client;
int mcast_sockfd;
int mcast_port;
struct sockaddr_in sa_mcast;
struct ip_mreq mcastaddr;
char *data_buffer;
int data_buffer_size;
/* For options access */
struct mtftp_data *mtftp_data;
struct mtftp_thread *next;
};
/*
* Functions defined in tftpd_file.c
*/
struct mtftp_data *tftpd_mtftp_init(char *filename);
int tftpd_mtftp_clean(struct mtftp_data *data);
void *tftpd_mtftp_server(void *arg);
void tftpd_mtftp_kill_threads(struct mtftp_data *data);
#endif

View File

@ -0,0 +1,315 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* tftpd_pcre.c
* functions to remmap file name requested by tftp clients according
* to regular expression rules
*
* $Id: tftpd_pcre.c,v 1.2 2003/04/25 00:16:19 jp Exp $
*
* Copyright (c) 2003 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* The PCRE code is provided by Jeff Miller <jeff.miller@transact.com.au>
*
* Copyright (c) 2003 Jeff Miller <jeff.miller@transact.com.au>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include "config.h"
#if HAVE_PCRE
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "tftp_def.h"
#include "config.h"
#include "logger.h"
#include "tftpd_pcre.h"
/*
* number of elements in vector to hold substring info
*/
#define OVECCOUNT 30
/*
* define the pattern for substitutions
* $0 whole string
* $1 to $9 substring 1 - 9
*/
/* create a pattern list from a file */
/* return 0 on success, -1 otherwise */
tftpd_pcre_self_t *tftpd_pcre_open(char *filename)
{
int linecount;
int erroffset;
int matches;
int ovector[OVECCOUNT];
char line[MAXLEN];
const char *error;
FILE *fh;
int subnum;
char **substrlist;
pcre *file_re;
pcre_extra *file_pe;
tftpd_pcre_self_t *self;
tftpd_pcre_pattern_t *pat, **curpatp;
/* open file */
if ((fh = fopen(filename, "r")) == NULL)
{
logger(LOG_ERR, "Cannot open %s for reading: %s",
filename, strerror(errno));
return NULL;
}
/* compile and study pattern for lines */
logger(LOG_DEBUG, "Using file pattern %s", TFTPD_PCRE_FILE_PATTERN);
if ((file_re = pcre_compile(TFTPD_PCRE_FILE_PATTERN, 0,
&error, &erroffset, NULL)) == NULL)
{
logger(LOG_ERR, "PCRE file pattern failed to compile");
return NULL;
}
file_pe = pcre_study(file_re, 0, &error);
if (error != NULL)
{
logger(LOG_ERR, "PCRE file pattern failed to study");
return NULL;
}
/* allocate header and copy info */
if ((self = calloc(1, sizeof(tftpd_pcre_self_t))) == NULL)
{
logger(LOG_ERR, "calloc filed");
return NULL;
}
self->lock = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
Strncpy(self->filename, filename, MAXLEN);
/* read patterns */
for (linecount = 1, curpatp = &self->list;
fgets(line, MAXLEN, fh) != NULL;
linecount++, curpatp = &pat->next)
{
logger(LOG_DEBUG,"file: %s line: %d value: %s",
filename, linecount, line);
/* allocate space for pattern info */
if ((pat = (tftpd_pcre_pattern_t *)calloc(1,sizeof(tftpd_pcre_pattern_t))) == NULL)
{
tftpd_pcre_close(self);
return NULL;
}
*curpatp = pat;
/* for each pattern read, compile and store the pattern */
matches = pcre_exec(file_re, file_pe, line, (int)(strlen(line)),
0, 0, ovector, OVECCOUNT);
/* log substring to help with debugging */
pcre_get_substring_list(line, ovector, matches, (const char ***)&substrlist);
for(subnum = 0; subnum <= matches; subnum++)
{
logger(LOG_DEBUG,"file: %s line: %d substring: %d value: %s",
filename, linecount, subnum, substrlist[subnum]);
}
pcre_free_substring_list((const char **)substrlist);
if (matches < 2)
{
logger(LOG_ERR, "error with pattern in file \"%s\" line %d",
filename, linecount);
tftpd_pcre_close(self);
pcre_free(file_re);
pcre_free(file_pe);
return NULL;
}
/* remember line number */
pat->linenum = linecount;
/* extract left side */
pcre_get_substring(line, ovector, matches, 1, (const char **)&pat->pattern);
/* extract right side */
pcre_get_substring(line, ovector, matches, 2, (const char **)&pat->right_str);
logger(LOG_DEBUG,"pattern: %s right_str: %s", pat->pattern, pat->right_str);
if ((pat->left_re = pcre_compile(pat->pattern, 0,
&error, &erroffset, NULL)) == NULL)
{
/* compilation failed*/
logger(LOG_ERR,
"PCRE compilation failed in file \"%s\" line %d at %d: %s",
filename, linecount,
erroffset, error);
/* close file */
fclose(fh);
/* clean up */
tftpd_pcre_close(self);
return NULL;
}
/* we're going to be using this pattern a fair bit so lets study it */
pat->left_pe = pcre_study(pat->left_re, 0, &error);
if (error != NULL)
{
logger(LOG_ERR,
"PCRE study failed in file \"%s\" line %d: %s",
filename, linecount,
error);
/* close file */
fclose(fh);
/* cleanup */
tftpd_pcre_close(self);
return NULL;
}
}
/* clean up */
pcre_free(file_re);
pcre_free(file_pe);
/* close file */
fclose(fh);
return self;
}
/* return filename being used */
/* returning a char point directly is a little risking when
* using thread, but as we're using this before threads
* are created we should be able to get away with it
*/
char *tftpd_pcre_getfilename(tftpd_pcre_self_t *self)
{
return self->filename;
}
/* this is a utility function used to make the actual substitution*/
int tftpd_pcre_makesub(struct tftpd_pcre_pattern *pat,
char *outstr, int outsize,
char *str,
int *ovector, int matches)
{
char *chp, *outchp;
const char *tmpstr;
int rc;
/* $0 - whole string, $1-$9 substring 1-9 */
for (chp = pat->right_str, outchp = outstr;
(*chp != '\0') && (outchp - outstr < outsize);
chp++)
{
if ((*chp == '$') && (*(chp+1) >= '0') && (*(chp+1) <= '9'))
{
chp++; /* point to value indicating substring */
rc = pcre_get_substring(str, ovector, matches, *chp - 0x30, &tmpstr);
/* found string */
if (rc > 0)
{
Strncpy(outchp, tmpstr, rc);
outchp += rc;
pcre_free_substring(tmpstr);
continue;
}
/* erro condition */
switch (rc)
{
case PCRE_ERROR_NOMEMORY:
logger(LOG_ERR, "PCRE out of memory");
break;
case PCRE_ERROR_NOSUBSTRING:
logger(LOG_ERR,
"PCRE attempted substitution failed for \"%s\" on pattern %d",
str, pat->linenum);
break;
}
}
else
{
*outchp = *chp;
outchp++;
}
}
*outchp = '\0';
return 0;
}
/* search for a replacement and return a string after substituation */
/* if no match is found return -1 */
int tftpd_pcre_sub(tftpd_pcre_self_t *self, char *outstr, int outlen, char *str)
{
int rc;
int ovector[OVECCOUNT];
int matches;
tftpd_pcre_pattern_t *pat;
/* lock for duration */
pthread_mutex_lock(&self->lock);
logger(LOG_DEBUG, "Looking to match \"%s\"", str);
/* interate over pattern list */
for(pat = self->list; pat != NULL; pat = pat->next)
{
logger(LOG_DEBUG,"Attempting to match \"%s\"", pat->pattern);
/* attempt match */
matches = pcre_exec(pat->left_re, pat->left_pe,
str, (int)(strlen(str)),
0, 0,
ovector, OVECCOUNT);
/* no match so we try again */
if (matches == PCRE_ERROR_NOMATCH)
continue;
/* error in making a match - log and attempt to continue */
if (matches < 0)
{
logger(LOG_WARNING,
"PCRE Matching error %d", matches);
continue;
}
/* we have a match - carry out substitution */
logger(LOG_DEBUG,"Pattern \"%s\" matches", pat->pattern);
rc = tftpd_pcre_makesub(pat,
outstr, outlen,
str,
ovector, matches);
logger(LOG_DEBUG,"outstr: \"%s\"", outstr);
pthread_mutex_unlock(&self->lock);
return 0;
}
logger(LOG_DEBUG, "Failed to match \"%s\"", str);
pthread_mutex_unlock(&self->lock);
return -1;
}
/* clean up and displose of anything we set up*/
void tftpd_pcre_close(tftpd_pcre_self_t *self)
{
tftpd_pcre_pattern_t *next, *cur;
/* free up list */
pthread_mutex_lock(&self->lock);
cur = self->list;
while (cur != NULL)
{
next = cur->next;
pcre_free_substring(cur->pattern);
pcre_free(cur->left_re);
pcre_free(cur->left_pe);
pcre_free_substring(cur->right_str);
free(cur);
cur = next;
}
pthread_mutex_unlock(&self->lock);
free(self);
}
#endif

View File

@ -0,0 +1,73 @@
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
/*
* tftpd_pcre.h
*
* $Id: tftpd_pcre.h,v 1.1 2003/02/21 05:06:06 jp Exp $
*
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
* and Remi Lefebvre <remi@debian.org>
*
* The PCRE code is provided by Jeff Miller <jeff.miller@transact.com.au>
*
* Copyright (c) 2003 Jeff Miller <jeff.miller@transact.com.au>
*
* atftp is free software; you can redistribute them and/or modify them
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef TFTPD_PCRE_H
#define TFTPD_PCRE_H
#include <pthread.h>
#include <pcre.h>
#include "tftpd.h"
/* Carry out filename substitution
* example, pattern
* (name) sur($1)
*
* for the requested file "filename" would give "surname"
*
*/
/*
* for when we read files, what is the format of a line
* pattern [whitespace] replacement_string
*/
#define TFTPD_PCRE_FILE_PATTERN "^(\\S+)\\s+(\\S+)$"
/*
* Definition of struct to hold patterns
*/
struct tftpd_pcre_pattern
{
unsigned int linenum;
char *pattern;
pcre *left_re;
pcre_extra *left_pe;
char *right_str;
struct tftpd_pcre_pattern *next;
};
typedef struct tftpd_pcre_pattern tftpd_pcre_pattern_t;
struct tftpd_pcre_self
{
pthread_mutex_t lock;
char filename[MAXLEN];
struct tftpd_pcre_pattern *list;
};
typedef struct tftpd_pcre_self tftpd_pcre_self_t;
/* function prototypes */
tftpd_pcre_self_t *tftpd_pcre_open(char *filename);
char *tftpd_pcre_getfilename(tftpd_pcre_self_t *self);
int tftpd_pcre_sub(tftpd_pcre_self_t *self, char *outstr, int outlen, char *str);
void tftpd_pcre_close(tftpd_pcre_self_t *self);
#endif